# Land.com Rural Land & Farm Scraper (US) (`scrapyx/land-com-properties-scraper`) Actor

Scrapes rural land, farms, ranches and acreage for sale from Land.com, the largest US rural-property marketplace. Search any state and property type; returns price, full address, description and seller contact, with an optional detail pass for acreage and lot specs.

- **URL**: https://apify.com/scrapyx/land-com-properties-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Land.com Rural Land & Farm Scraper (US)

Scrapes rural land, farms, ranches and acreage for sale from
**[Land.com](https://www.land.com)** — the largest US rural-property
marketplace. A data-type none of this portfolio's other 15 real-estate
actors cover: they are all houses/apartments, this is raw land.

Public data only. No login, no cookies, no browser.

### The one thing you need to know before using this

**The search AND detail pages are behind Akamai Bot Manager**, which serves
a disguised HTTP 200 "behavioral content" challenge — it looks like a
normal response by status code alone. Only 3 of 9 TLS profiles tried this
session bypass it on **both** surfaces (`chrome99_android`, `safari17_0`,
`safari17_2_ios`) — this actor's pool leads with that intersection.

More importantly: recon evidence points to the gate reacting to **request
velocity from one IP**, not just TLS fingerprint — a clean profile ladder
degraded to fully blocked after a burst of unpaced requests and did not
recover on its own. This actor defaults to Apify **Residential proxy**
(rotates exit IPs) plus a conservative pace (`minRequestInterval=2s`,
`maxConcurrency=3`) for that reason — turning either down is likely to
reproduce the block. See [`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) §3 for
the full ladder and the IP-reputation evidence.

### What you get

Three record types share one dataset, told apart by `recordType`.

#### `PROPERTY` — one row per listing

Search rows (`listing`) already carry price, full street address,
description and the listing agent's name/phone — no detail fetch is
strictly required. Turn on **Fetch listing detail pages** to also attach
`propertyDetail`, which adds fields genuinely absent from search: acreage,
lot description, terrain, utilities and more, under
`propertyDetail.mainEntity.additionalProperty`.

#### `SEARCH_SUMMARY` — one row per (state, property type) searched

Pages fetched, rows returned, a best-effort total (see notes below), and
`pathApplied` — whether Land.com actually searched what was asked (see
"Notes on reliability").

#### `ERROR` — one row per input that failed

So every entry in **Searches** maps to at least one output row, success or
failure.

### Input

| Field | What it does |
| --- | --- |
| **Searches** | list of `{state, propertyType}` — `state` is a full US state name (`"Texas"`, `"Montana"`); `propertyType` defaults to `all-land` |
| **Fetch listing detail pages** | adds acreage/lot-description/terrain fields not present in search (default on) |
| **Max properties / max pages per search** | pagination caps — Land.com itself hard-clamps at 20 pages / 500 rows per search regardless |
| **Max concurrent requests / Min seconds between requests** | kept conservative by default — see the WAF note above |

#### Example

```json
{
  "queries": [
    {"state": "Texas", "propertyType": "ranches"},
    {"state": "Montana"}
  ],
  "includePropertyDetails": true,
  "maxItems": 100
}
```

#### Valid `propertyType` values

`all-land`, `cheap-land`, `undeveloped-land`, `farms`, `ranches`,
`houses-with-land`. See `CRAWLING_METHOD.md` §6 for how each was verified.

### Notes on reliability

- **Active WAF (Akamai)** — see above. Residential proxy is hardcoded as a
  fallback in `main.py`, not only the input schema default, so an API/CLI
  run that omits `proxyConfiguration` never silently goes direct. Cloud
  verification found success rate varies by `propertyType`: `cheap-land`
  and `ranches` cleared within a few retries every time tried, while
  `all-land` (the single broadest, highest-value search on the site) is
  consistently the hardest to get through and may need several actor runs.
  See `CRAWLING_METHOD.md` §3 for the evidence.
- **A 500-row-per-search ceiling is real and self-reported**, not a bug:
  Land.com clamps every (state, propertyType) combination to 20 pages
  regardless of true inventory (Texas alone has tens of thousands of
  listings). Query more `propertyType` values for the same state to reach
  further into a large state's inventory.
- **A category that doesn't apply fails clean, not silently-wrong**: an
  unrecognised `propertyType` widens to the state's full unfiltered
  baseline upstream (verified live) — this actor detects that via Land.com's
  own canonical-URL field and refuses to page further, reporting
  `pathApplied: false` with zero `PROPERTY` rows for that entry, rather than
  shipping mislabeled data.
- **Multi-word state slugs are unverified** (only `Texas` was confirmed
  live this session) — a wrong guess answers a clean HTTP 400
  (`invalid_path`), never silent-wrong data. See `CRAWLING_METHOD.md` §7.
- **`estimatedTotalListings` is best-effort**, parsed from page copy, not a
  structured field — Land.com's JSON-LD never exposes a true total. Not
  used for any pagination logic.
- **A de-listed detail page answers a clean HTTP 404** — the search row is
  still emitted, with `propertyDetail: null`.

### Output envelope

Every record carries `_input`, `_source` and `_scrapedAt`. Upstream field
names pass through **verbatim** under `listing` (and `propertyDetail` when
requested) — no renaming.

See [`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) for the full
reverse-engineering trail, including the complete TLS profile ladder, the
IP-reputation evidence, and what was NOT verified this session.

# Actor input Schema

## `queries` (type: `array`):

One search per entry, each with its own SEARCH\_SUMMARY row. 'state' is a full US state name (e.g. 'Texas', 'New Mexico') -- multi-word state slugs are unverified against Land.com's own URL grammar this session, see README. 'propertyType' defaults to 'all-land' if omitted; other verified values are 'cheap-land', 'undeveloped-land', 'farms', 'ranches', 'houses-with-land'. Example: \[{"state": "Texas", "propertyType": "ranches"}, {"state": "Montana"}]

## `includePropertyDetails` (type: `boolean`):

Also fetch each listing's detail page for fields not present in search results (acreage, lot description, terrain, utilities, additional photos). Costs one extra request per listing. On by default because these fields are the main reason to use a rural-land actor over a generic listing scraper.

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

Stop paginating a search after this many properties. Set to 0 for unlimited (still bounded by Max pages and Land.com's own hard ceiling of 20 pages / 500 listings per state+propertyType combination -- query more property types for the same state to reach more of a large state's inventory).

## `maxPages` (type: `integer`):

Hard cap on pagination depth, independent of maxItems. Land.com clamps every (state, propertyType) search to 20 pages (500 listings) regardless of the true total -- confirmed live, pages past that re-serve page 20's content forever -- so this actor never pages past 20 even if set higher.

## `maxConcurrency` (type: `integer`):

Upper bound on requests in flight at once, across searches and detail fetches. Kept low by default -- this target is behind an active, IP-reputation-sensitive Akamai bot-mitigation gate (see README) with only 3 confirmed-working TLS profiles.

## `minRequestInterval` (type: `integer`):

Paces request starts (not held inside a concurrency slot) rather than raw concurrency. Recon evidence this session is that this target's Akamai gate degrades under request-burst velocity from one IP -- this is the honest speed control once the rate cap binds, extra maxConcurrency buys nothing.

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

Land.com is behind Akamai Bot Manager, which serves a disguised HTTP 200 'behavioral content' challenge. This session's recon found the block IP-reputation sensitive (a clean TLS-profile ladder degraded to fully blocked after sustained unpaced requests from one IP and did not recover on its own) -- Apify Residential proxy, which rotates exit IPs, is the default for cloud runs.

## Actor input object example

```json
{
  "queries": [
    {
      "state": "Texas",
      "propertyType": "all-land"
    }
  ],
  "includePropertyDetails": true,
  "maxItems": 100,
  "maxPages": 20,
  "maxConcurrency": 3,
  "minRequestInterval": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "queries": [
        {
            "state": "Texas",
            "propertyType": "all-land"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/land-com-properties-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 = { "queries": [{
            "state": "Texas",
            "propertyType": "all-land",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/land-com-properties-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 '{
  "queries": [
    {
      "state": "Texas",
      "propertyType": "all-land"
    }
  ]
}' |
apify call scrapyx/land-com-properties-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/land-com-properties-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/l4b1WlFCRCngeWYaO/builds/H7gjKIOhAyGNmTtKz/openapi.json
