# Texas Tax Sale Monitor — 12 Counties, New Listings, No Login (`outstanding_vegetable/texas-tax-sale-monitor`) Actor

Watch tax sales in 12 Texas counties (Harris, Dallas, Travis, Bexar, Tarrant and more) and get only NEW or changed listings: address, cause number, adjudged value, minimum bid, sale date. Daily schedule. No login. MCP-ready. $20 per 1,000 alerts.

- **URL**: https://apify.com/outstanding\_vegetable/texas-tax-sale-monitor.md
- **Developed by:** [Peter Skotte](https://apify.com/outstanding_vegetable) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 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?

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

## Texas Tax Sale Monitor — new delinquent-tax sale listings, daily

Get a **daily feed of only the new or changed tax foreclosure sale listings** across the big Texas counties —
Harris, Dallas, Travis, Bexar, Tarrant, El Paso, Montgomery, Galveston and more — with the sale date, cause
number, appraisal-district account, address, adjudged value, minimum bid and a Google Maps link. The monitor
remembers every listing it has already reported, so a scheduled run emits just the delta (`new` or `updated`),
posts a summary to your webhook, and costs you only for what actually changed.

Built for distressed-property investors, wholesalers and title researchers who want every county's list the
day it is posted, not a week later from a paid aggregator.

### County coverage

| County (`counties` value) | Source | Fields | Typical posting cadence |
|---|---|---|---|
| `harris` | Harris County Tax Office listing (hctax.net) | full: address, cause, HCAD account, adjudged value, min bid, precinct, legal description, judgment date, tax years | Monthly sale on the first Tuesday; list posted ~3-4 weeks ahead, cancellations flagged up to sale day |
| `dallas` | Dallas County Sheriff online auction (RealAuction) | address, cause, DCAD account, adjudged value, est. min bid, sale number | First Tuesday; items appear ~3 weeks before the sale |
| `travis` | Travis County Constables online auction (RealAuction / realforeclose) | address, cause, TCAD account, adjudged value, est. min bid, precinct | First Tuesday; posted ~15 days ahead |
| `bexar` | Bexar County Clerk foreclosure map, tax layer (maps.bexar.org) | address, city, ZIP, cause (document number), school district, lat/lon | Updated twice a month: ~18 days before the sale and the Friday after |
| `tarrant` | Tarrant County Constable Pct. 3 monthly listing pages | cause number, TAD account, status (For Sale / Withdrawn) | Pages for the next 1-2 sale dates; no address or bid on the county page |
| `el-paso` | El Paso County online auction (RealAuction) | same as Dallas | First Tuesday; posted a few weeks ahead (not every month has a sale) |
| `montgomery` | Montgomery County Constables online auction (RealAuction / realforeclose) | same as Dallas | First Tuesday; not every month has a sale |
| `galveston`, `smith`, `nueces`, `cameron`, `hidalgo` | County online auction (RealAuction) | same as Dallas | First Tuesday |

Counties whose only public list is not machine-readable are **not** supported and are skipped with a warning:
**Fort Bend** (published only on the Linebarger portal, whose listing API has been returning server errors),
**Collin** (upcoming sales are posted at the courthouse only; the county site lists struck-off resales by
private offer only) and **Denton** (GovEase auction platform, listings behind a bidder login).

Texas law puts every tax sale on the **first Tuesday of the month, 10:00 to 16:00 local time**. A county with
no scheduled sale for the coming month simply returns zero listings until its next list is posted.

### How it works

1. Pulls the current sale list for each county in `counties` (all upcoming sale dates on the county's calendar).
2. Applies your `saleTypes`, `minBid` / `maxBid` and `propertyTypes` filters.
3. Compares each listing against the monitor's saved state (`county + cause number + account`).
4. Emits a record when the listing is unseen (`changeType: "new"`) or when its **sale date, minimum bid or
   status** changed since it was last reported (`changeType: "updated"`).
5. Saves the updated state, then POSTs a run summary to `webhookUrl` if set.

State lives in a named key-value store `txtaxsale-monitor-<hash of monitorId>` in your Apify account, capped
at 50,000 listings (oldest are dropped). Delete the store to reset a monitor.

### Input

| Field | Default | Notes |
|---|---|---|
| `counties` | `["harris","dallas"]` | Any of the county keys in the table above. Names like `"El Paso"` or `"Harris County"` are accepted |
| `saleTypes` | `["tax","resale","struck-off"]` | `tax` original foreclosure sale, `resale` struck-off property re-offered, `struck-off` bid off to the county, `execution` Harris County execution / order-of-sale listings. Empty = all |
| `minBid`, `maxBid` | unset | Filter on the published minimum / opening bid in USD. Listings with no published bid are excluded when either is set |
| `propertyTypes` | `[]` | Substring match on the property type where a source publishes one; listings without a type are kept |
| `maxNewPerCounty` | `10` | Cap per county per run. Anything beyond stays unseen and comes out next run |
| `maxItems` | `10` | Overall cap per run, shared round-robin across counties so every county is represented |
| `firstRunMode` | `emitAll` | `emitAll` reports every current listing on the first run; `baseline` records them silently |
| `webhookUrl` | `""` | Optional POST target for the run summary |
| `monitorId` | `default` | One state store per ID — run several watchlists side by side |
| `proxyConfiguration` | none | All sources are reachable from the Apify platform without a proxy |

### Recommended setup for a daily feed

1. Create a task with your counties and bid range, and set `monitorId` (for example `dfw-under-50k`).
2. **First run: set `firstRunMode` to `baseline`.** This records every listing currently posted, emits
   nothing, and stops day one from being a 300-row backlog. Skip this if you *want* the full current list.
3. Set `maxItems` and `maxNewPerCounty` to 500+ so a busy posting day is never truncated.
4. **Schedule the task daily at 07:00 America/Chicago.** Counties post and update their lists during the
   business day; a morning run catches everything from the previous day. Around the third week of each
   month (when most lists for the first-Tuesday sale go up) a second afternoon run is worth the start fee.
5. Point `webhookUrl` at Slack, Zapier, Make, or your own endpoint.

The default settings (`{}`) run in `emitAll` mode for Harris and Dallas so you see real output on the first try.

#### Example: DFW and Houston, bids under $50k

```json
{
  "counties": ["harris", "dallas", "tarrant", "montgomery", "galveston"],
  "saleTypes": ["tax", "resale"],
  "maxBid": 50000,
  "maxNewPerCounty": 500,
  "maxItems": 2000,
  "firstRunMode": "baseline",
  "webhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
  "monitorId": "dfw-houston-under-50k"
}
```

### Output

One record per new or updated listing:

```json
{
  "county": "Harris",
  "state": "TX",
  "saleDate": "2026-10-06",
  "saleType": "tax",
  "status": "For Sale",
  "causeNumber": "201568888",
  "accountNumber": "0720160010046",
  "address": "4114 E TOLIVER ST",
  "city": "HOUSTON",
  "zip": "77016-6239",
  "legalDescription": "The West 50 feet of Lot 46 of Cranford Place, a subdivision in Harris County, Texas ...",
  "propertyType": null,
  "adjudgedValue": 64263,
  "minimumBid": 18088.24,
  "estimatedValue": null,
  "precinct": "Precinct 1",
  "saleNumber": "1",
  "judgmentDate": "2016-06-29",
  "taxYears": "2013 - 2015",
  "schoolDistrict": null,
  "source": "Harris County Tax Office (hctax.net)",
  "sourceUrl": "https://www.hctax.net/Property/listings/taxsalelisting",
  "detailUrl": "https://www.hctax.net/Property/listings/saledetail?account=0720160010046",
  "cadUrl": "https://public.hcad.org/records/outsider/hc.asp?acct=0720160010046",
  "mapUrl": "https://www.google.com/maps/search/?api=1&query=4114%20E%20TOLIVER%20ST%2C%20HOUSTON%2C%20TX%2C%2077016-6239",
  "changeType": "new",
  "firstSeenAt": "2026-09-28T19:23:10.318Z",
  "monitorId": "default"
}
```

`firstSeenAt` is the run in which the listing was first reported; `updated` records keep their original
`firstSeenAt`. Fields a county does not publish are `null` (Bexar has no bids, Tarrant has no address or bid;
open `sourceUrl` or look the account up at the appraisal district for those).

### Webhook payload

POSTed once per run as `application/json`, also saved as the `SUMMARY` record in the run's key-value store:

```json
{
  "monitorId": "dfw-houston-under-50k",
  "runAt": "2026-09-29T12:00:03.118Z",
  "newCount": 4,
  "updatedCount": 1,
  "counties": { "harris": { "scanned": 231, "emitted": 3 }, "dallas": { "scanned": 33, "emitted": 2 } },
  "seenTotal": 812,
  "baseline": false,
  "listings": [ { "...first 50 records, same shape as the dataset..." } ]
}
```

### Pricing

Pay per event: a small start fee plus a per-listing fee **only for records emitted**. A daily monitor that
finds nothing new costs just the start fee.

### Notes

- A listing counts as `updated` when its sale date, minimum bid or status changes (for example a Harris
  County cancellation or a Tarrant County withdrawal).
- RealAuction counties are read from the auction calendar for the current month and the next two, so a list
  posted early for the following month is picked up as soon as it appears.
- Sources are public county and constable sites; the actor paces requests and needs no login or API key.

# Actor input Schema

## `counties` (type: `array`):

Texas counties to monitor. Supported: harris, dallas, travis, bexar, tarrant, el-paso, montgomery, galveston, smith, nueces, cameron, hidalgo. Unsupported names are skipped with a warning (see README for why fort-bend, collin and denton are not available).

## `saleTypes` (type: `array`):

Which listings to include: tax (original tax foreclosure sale), resale (struck-off property re-offered by the taxing units), struck-off (properties bid off to the county and available for private offer), execution (Harris County execution/order-of-sale listings). Empty = all types.

## `minBid` (type: `integer`):

Only listings whose minimum/opening bid is at least this amount. Listings without a published bid are excluded when this is set.

## `maxBid` (type: `integer`):

Only listings whose minimum/opening bid is at most this amount. Listings without a published bid are excluded when this is set.

## `propertyTypes` (type: `array`):

Case-insensitive substrings matched against the property type when the source publishes one (e.g. residential, commercial, land). Listings with no property type are kept. Empty = all.

## `maxNewPerCounty` (type: `integer`):

Cap on new or updated listings emitted for each county in one run. Anything beyond the cap stays unseen and comes out on the next run.

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

Overall cap on records emitted in one run, shared round-robin across counties. Raise it to 500+ for a production schedule.

## `firstRunMode` (type: `string`):

emitAll reports every current listing on the very first run of a monitorId; baseline records them silently so only listings posted after that run are reported.

## `webhookUrl` (type: `string`):

Optional. A JSON summary with the new/updated listings (up to 50) is POSTed here at the end of every run.

## `monitorId` (type: `string`):

Name of this watchlist. Each ID keeps its own memory of seen listings, so you can run several monitors with different counties or bid ranges side by side.

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

Optional. The sources are public county sites reachable without a proxy; enable Apify residential proxies only if a county starts blocking the platform.

## Actor input object example

```json
{
  "counties": [
    "harris",
    "dallas"
  ],
  "saleTypes": [
    "tax",
    "resale",
    "struck-off"
  ],
  "propertyTypes": [],
  "maxNewPerCounty": 10,
  "maxItems": 10,
  "firstRunMode": "emitAll",
  "webhookUrl": "",
  "monitorId": "default"
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset of new or updated Texas tax sale listings found in this run (JSON).

# 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 = {
    "counties": [
        "harris",
        "dallas"
    ],
    "saleTypes": [
        "tax",
        "resale",
        "struck-off"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("outstanding_vegetable/texas-tax-sale-monitor").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 = {
    "counties": [
        "harris",
        "dallas",
    ],
    "saleTypes": [
        "tax",
        "resale",
        "struck-off",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("outstanding_vegetable/texas-tax-sale-monitor").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 '{
  "counties": [
    "harris",
    "dallas"
  ],
  "saleTypes": [
    "tax",
    "resale",
    "struck-off"
  ]
}' |
apify call outstanding_vegetable/texas-tax-sale-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,outstanding_vegetable/texas-tax-sale-monitor"
        }
    }
}
```

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/l8Jxg7LTKylokKiFT/builds/CbVauJ635fqPs3EBi/openapi.json
