# Bid4Assets Scraper - County Tax Deed & Sheriff Sale Parcels (`scrapersdelight/bid4assets-tax-sale-scraper`) Actor

From $4.00 per 1,000 lots, no start fee. Every county tax-deed parcel and sheriff-sale property on Bid4Assets: 15,029 lots across 55 counties, measured. APN, minimum bid, close date, plaintiff, defendant, court case, debt amount. Optional per-parcel assessed value and due-diligence documents.

- **URL**: https://apify.com/scrapersdelight/bid4assets-tax-sale-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Real estate, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 per lot returneds

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

## Bid4Assets Scraper — County Tax Deed & Sheriff Sale Parcels

Every lot on [Bid4Assets](https://www.bid4assets.com), at the grain a tax-deed fund actually
underwrites: **one row = one parcel**, not one county sale.

**$4.00 per 1,000 lots. No run-start fee. No login, no cookie, no API key.**

Bid4Assets is where US county tax collectors and sheriffs sell tax-defaulted parcels and
foreclosed property. On **2026-09-18** this Actor pulled the whole site and counted:

| | storefronts | lots | counties |
|---|---:|---:|---:|
| County tax sales | 47 | **14,304** | 32 |
| Sheriff & foreclosure sales | 36 | **725** | 29 |
| **Total** | **83** | **15,029** | **55** |

Across CA, FL, LA, NV, OK, PA, WA and WI. Those are counted numbers — the Actor summed each
collection's own declared total and verified 15,029 distinct auction IDs, no duplicates, no short
reads. Coverage changes as counties open and close sales; the run's `RUN_SUMMARY` always records
what the site declared against what was actually read.

***

### What one row is

One lot. Either a **tax-defaulted parcel** from a county tax-collector sale (upset, judicial,
repository, tax-defaulted, tax-title, adjudicated), or a **property in a sheriff / mortgage
foreclosure sale**. Both halves land in one dataset with one schema and one sort order, so you can
work a single close-date-ordered list.

#### A real tax-sale row, with per-parcel enrichment on

Taken verbatim from a run on 2026-09-18 (`auctionId` 1261009, Monterey County CA), trimmed only
where marked:

```json
{
  "auctionId": 1261009,
  "recordType": "tax-sale-parcel",
  "saleCategory": "tax-sale",
  "saleType": "Tax Defaulted Property Sale",
  "title": "Monterey County, CA: APN:003-145-002-000-Item No: 1",
  "county": "Monterey County",
  "state": "CA",
  "stateName": "California",
  "city": "Salinas",
  "parcelId": "003-145-002-000",
  "address": "116 N Main St | Salinas, CA",
  "streetAddress": "116 N Main St",
  "postalCode": null,
  "minimumBid": 224300,
  "currentBid": 224300,
  "bidIncrement": 100,
  "buyerPremiumPercent": 3,
  "auctionStatus": "withdrawn",
  "statusLabel": "Closed",
  "isWithdrawn": true,
  "auctionOpenDate": "2026-03-21T11:00:00",
  "actualCloseDate": "2026-03-25T16:00:00",
  "saleDate": "2026-03-25",
  "assessedTotal": 236752,
  "landValue": 159809,
  "improvementsValue": 76943,
  "assessedYear": "2025-2026",
  "equityMarginEstimate": 12452,
  "discountVsAssessedPct": 5.3,
  "countyItemNumber": "1",
  "parcelMapUrl": "https://www.bid4assets.com/info/sfid17554/Maps/003-145-002.pdf",
  "gisUrl": "https://maps.co.monterey.ca.us/wab/parcelreportwebapp/?marker=-121.654,36.680…",
  "dueDiligenceDocuments": [
    { "name": null, "url": "https://www.bid4assets.com/info/sfid17554/Legals/003-145-001.pdf" },
    { "name": "County Terms of Sale", "url": "https://www.bid4assets.com/storefront/viewDueDiligence/24176?key=85082495-…" },
    { "name": "California Revenue and Taxation Code", "url": "https://www.bid4assets.com/storefront/viewDueDiligence/24177?key=36e30c23-…" }
  ],
  "itemSpecifics": {
    "Parcel Information": { "APN": "003-145-002-000", "County Item No": "1", "Address": "116 N Main St | Salinas, CA", "Community": "Salinas" },
    "Assessed Values": { "Assessed Values From": "2025-2026", "Improvements": "$76,943.00", "Land Value": "$159,809.00", "Total Assessed Values": "$236,752.00" },
    "Tax Bill": { "Ad Valorem": "$236,752.00" }
  },
  "sellerName": "Monterey County",
  "storefrontUrl": "https://www.bid4assets.com/storefront/MontereyMar26",
  "saleWindow": "March 21st - 25th",
  "sourceUrl": "https://www.bid4assets.com/auction/index/1261009",
  "scrapedAt": "2026-09-18T19:37:01.569Z"
}
```

Note `isWithdrawn: true` while the site's own status still reads `Closed`. That lot's owner paid
the back taxes and the county pulled it. See *The withdrawn problem* below.

#### A real sheriff-sale row

From the same day (`auctionId` 1305945), a lot that was still in preview:

```json
{
  "auctionId": 1305945,
  "recordType": "sheriff-sale-property",
  "saleCategory": "sheriff-sale",
  "saleType": "HUD Foreclosure Sale",
  "title": "Philadelphia County, PA Sale: 1913 N. Hancock Street",
  "county": "Philadelphia County",
  "state": "PA",
  "city": "Philadelphia",
  "parcelId": "183261000",
  "address": "1913 N. Hancock Street, Philadelphia PA 19122",
  "streetAddress": "1913 N. Hancock Street",
  "postalCode": "19122",
  "minimumBid": 242101,
  "currentBid": 242101,
  "bidCount": 0,
  "attorneyHasSetMinimumBid": true,
  "auctionStatus": "preview",
  "statusLabel": "Preview",
  "auctionOpenDate": "2026-09-21T10:00:00",
  "saleDate": "2026-09-21",
  "timeLeftSeconds": 249560,
  "plaintiff": "Secretary of Housing and Urban Development",
  "defendant": "Rosa Romero",
  "attorney": "Stephen Hladik",
  "courtCase": "26-01126-1",
  "debtAmount": 242100.57,
  "township": "Philadelphia",
  "sellerName": "Philadelphia County",
  "storefrontUrl": "https://www.bid4assets.com/phillyhudpa",
  "sourceUrl": "https://www.bid4assets.com/auction/index/1305945",
  "scrapedAt": "2026-09-18T19:40:39.645Z"
}
```

***

### Every field, with its measured fill

Counts are from real captured bytes on 2026-09-18, recomputed by `offline_validate.mjs` on every
run so these numbers cannot drift away from the data. `"N/A"`, `"-"`, `""` and link placeholders
like `"Click Here"` are **not** counted as filled.

#### On every lot, both halves — 0 extra requests (N = 15,029)

| field | fill | what it is |
|---|---|---|
| `auctionId` | 15,029 / 100.0% | Bid4Assets' own lot id |
| `recordType` | 15,029 / 100.0% | `tax-sale-parcel` or `sheriff-sale-property` |
| `saleCategory` | 15,029 / 100.0% | `tax-sale` or `sheriff-sale` |
| `saleType` | 15,028 / 100.0% | Tax Upset / Judicial / Repository / Defaulted / Title / Adjudicated / Sheriff / HUD |
| `title` | 15,029 / 100.0% | the lot title, with any `***MARKER***` prefix lifted out |
| `county` | 15,029 / **100.0%** | in the site's own wording — "Vermilion **Parish**", never an invented "County" |
| `state`, `stateName` | 15,029 / 100.0% | two-letter code and full name |
| `parcelId` | 14,835 / **98.7%** | APN / PIN / parcel number, read out of the lot title |
| `minimumBid` | 14,999 / 99.8% | opening bid. The 30 nulls are sheriff lots whose attorney has not filed one |
| `currentBid` | 14,999 / 99.8% | |
| `bidIncrement` | 14,304 tax / 100.0% | tax rows only |
| `buyerPremiumPercent` | 14,304 tax / 100.0% | tax rows only; 287 counties charge a real 0% |
| `auctionStatus` | 15,029 / 100.0% | `preview` · `open` · `sold` · `closed` · `withdrawn` · `postponed` · `stayed` · `in_person` |
| `statusLabel` | 15,029 / 100.0% | the site's own wording, markup stripped |
| `isWithdrawn` | 15,029 / 100.0% | true on 5,760 lots (38.3%) |
| `isSealedBid` | 15,029 / 100.0% | |
| `auctionOpenDate`, `auctionCloseDate`, `actualCloseDate`, `saleDate` | 15,029 / 100.0% | the site's naive local timestamps, kept verbatim |
| `timeLeftSeconds` | 2,498 / 16.6% | set on exactly the `preview` + `open` lots; a withdrawn or stayed lot counts down to nothing |
| `winningBid` | 1,683 / 11.2% | only on lots that sold |
| `sellerName`, `storefrontName`, `storefrontSlug`, `storefrontUrl` | 15,029 / 100.0% | the county office running the sale |
| `saleWindow` | 14,304 tax / 100.0% | e.g. "September 18th - 21st", off the site's own calendar |
| `imageUrl` | 14,304 tax / 100.0% | |
| `sourceUrl`, `scrapedAt` | 15,029 / 100.0% | |

#### Sheriff / foreclosure rows only — the court record (N = 725)

| field | fill |
|---|---|
| `address`, `streetAddress`, `courtCase`, `bidCount`, `attorneyHasSetMinimumBid` | 725 / **100.0%** |
| `defendant` | 724 / 99.9% |
| `plaintiff` | 710 / 97.9% |
| `attorney` | 694 / 95.7% |
| `parcelId` | 538 / 74.2% |
| `township` | 527 / 72.7% |
| `debtAmount` | 519 / 71.6% |
| `sheriffNumber` | 481 / 66.3% |
| `postalCode` | 461 / 63.6% |
| `city` | 352 / 48.6% |

#### With `includeParcelDetails` on — 1 extra request per delivered lot

Counted over **92 real detail pages**, two from each of the 47 tax storefronts. These are
county-optional fields: a county that does not publish an assessment leaves it `null`, never `0`.

| field | fill | note |
|---|---|---|
| `parcelId` | 92 / **100.0%** | the county's own table overrides the title-derived value |
| `address` | 92 / 100.0% | situs address. Street number present on about half; 3 of 92 literally say "no site address" |
| `dueDiligenceDocuments` | 92 / 100.0% | 302 documents across the 92 pages — terms of sale, legals, maps, statutes |
| `itemSpecifics` | 92 / 100.0% | the county's raw table, verbatim: tax rate area, recording fee, exemptions, personal property value, special assessments, fixtures… |
| `sellerName` | 92 / 100.0% | |
| `streetAddress` | 89 / 96.7% | |
| `city` | 79 / 85.9% | |
| `situsLocation` | 74 / 80.4% | |
| `depositRequired` | 60 / 65.2% | |
| `bidCount` | 60 / 65.2% | the bid box is dropped on some closed lots |
| `countyItemNumber` | 59 / 64.1% | |
| `assessedTotal` | 54 / 58.7% | |
| `equityMarginEstimate`, `discountVsAssessedPct` | 54 / 58.7% | derived; `null` unless both the assessment and the opening bid are real |
| `parcelMapUrl` | 51 / 55.4% | |
| `legalDescription` | 50 / 54.3% | |
| `gisUrl` | 42 / 45.7% | several counties' GIS links embed the parcel's lat/long |
| `acreage` | 41 / 44.6% | |
| `landValue` | 32 / 34.8% | |
| `assessedYear` | 28 / 30.4% | |
| `postalCode` | 28 / 30.4% | |
| `improvementsValue` | 25 / 27.2% | |
| `zoning` | 22 / 23.9% | |
| `totalTaxBill` | 15 / 16.3% | |
| `irsLiens` | 13 / 14.1% | a federal lien survives a tax deed in most states — worth knowing before you bid |

The lot page prints a Minimum Bid on 60 of those 92 pages, and where it does it **equals the API's
`minimumBid` on 60 of 60**. The two surfaces agree.

Enrichment also works on the **sheriff half**, where it mostly fills in the address. Counted over
**56 real sheriff lot pages**, up to two from each of the 29 sheriff storefronts that currently list
lots:

| field | before | after |
|---|---|---|
| `situsLocation` | 0 / 0.0% | 55 / **98.2%** |
| `itemSpecifics` | 0 / 0.0% | 55 / 98.2% |
| `city` | 27 / 48.2% | 52 / **92.9%** |
| `postalCode` | 32 / 57.1% | 51 / **91.1%** |
| `depositRequired` | 0 / 0.0% | 46 / 82.1% |
| `legalDescription` | 0 / 0.0% | 23 / 41.1% |
| `parcelId` | 32 / 57.1% | 34 / 60.7% |

Sheriff lots have no assessed values, no acreage and no due-diligence documents on their pages — the
county publishes the court record instead, which is already in the base row.

***

### What this Actor does that the other one does not

There is exactly **one** other Actor in the Apify store that names this site — confirmed by
grepping all 12 store sitemaps (599,202 URLs), not just by searching. It reads the site's
`/api/search/process` index. That is a different corpus:

1. **Parcel-level, not sale-level.** The search index is an auction-*event* list. One of its rows
   reads *"NOTICE OF SALE: Clinton County, Pennsylvania — Live Tax Deed Sale, 366 Deeds"* — 366
   parcels compressed into one row with no APN and no address. This Actor enumerates the county
   storefront collections instead and returns each of the parcels.
2. **33× the corpus.** The search index reported **437** live auctions on 2026-09-18 and hard-caps
   at `total=999` on closed/all, so the sold and closed history — the comps a tax-deed fund
   underwrites against — cannot be enumerated through it at all. The storefront path returned
   **15,029** lots including 6,614 sold and closed ones.
3. **The sheriff-sale half.** 725 properties across 29 counties with `plaintiff`, `defendant`,
   `attorney`, `courtCase`, `debtAmount`, `sheriffNumber` and `township`. No other Actor ships
   these for this site.
4. **Per-parcel enrichment at no extra charge** — assessed values, legal description, acreage,
   zoning, IRS liens, the county parcel-map and GIS links, and every attached due-diligence
   document, all at the same per-lot price.
5. **`isWithdrawn`.** See below.
6. **A completeness assertion.** Every collection is floor-asserted against the API's *own*
   declared total and every sheriff page against its *own* `"Total"`. A short read aborts the run
   instead of shipping a quietly incomplete list. `RUN_SUMMARY` records the reconciliation.
7. **No run-start fee.** The other Actor charges $0.005 per GB of memory at start, on 4,096 MB —
   $0.02 before the first row.

#### The withdrawn problem

When a delinquent owner pays their back taxes before the sale, the county pulls the parcel. Bid4Assets
has **no status code for this**. It prefixes the lot's own *title* with `***Withdrawn***` and leaves
the structured row reading `statusID: 6 / "Closed"` — indistinguishable from a parcel that went to
auction and got no bid.

Measured: **5,760 of 15,029 lots — 38.3% — are withdrawn.** Six more are `***Stayed***`. Anyone
building comps off the raw status field is treating more than a third of the corpus as failed
auctions when the tax was simply paid.

This Actor lifts the marker out of the title, sets `auctionStatus` and `isWithdrawn` accordingly,
hands you a clean `title`, and gives you a switch (`excludeWithdrawn`) to drop them before you are
charged for them.

***

### Fields that are NOT shipped, and why

Every one of these was checked against the captured bytes and found empty. Shipping them would be a
column of nulls.

| not shipped | measured |
|---|---|
| `locatedCity`, `locatedState` (parcel API) | filled on **0 / 14,304** rows. City comes from the detail page instead |
| `SaleType` (sheriff rows) | 0 / 725 |
| `TimeZoneSuffix`, `CustomStayedStatusLabel`, `ImagesXML`, `DueDiligenceXML` (sheriff rows) | 0 / 725 each |
| `Images`, `DueDiligenceItems` (sheriff rows) | present as empty arrays on 725 / 725 |
| `reserve` (tax rows) | `0` on all 14,304 |
| beds, baths, square footage, year built, property type | **Bid4Assets publishes none of these on any surface.** Zero matching keys in the parcel API, the sheriff rows, or the Item Specifics tables across 92 detail pages. A competing aggregator lists them in its schema; on this source they would be null |
| `normalizedAddress` | a derived string; `streetAddress` / `city` / `postalCode` are shipped split instead |
| `daysUntilAuction` | shipped as `timeLeftSeconds`, the same fact at finer resolution |

***

### Inputs

| input | what it does |
|---|---|
| `saleTypes` | `tax-sale`, `sheriff-sale`, or both (default) |
| `states` | two-letter codes. Applied to the site's index page **before** any storefront is fetched |
| `counties` | county / parish names, with or without the suffix |
| `storefronts` | pin to named sales by slug, path or URL |
| `auctionStatus` | any of the eight statuses |
| `excludeWithdrawn` | drop the 38% of lots the county pulled |
| `closingAfter` / `closingBefore` | `YYYY-MM-DD`, on the sale date |
| `minMinimumBid` / `maxMinimumBid` | opening-bid range |
| `includeParcelDetails` | open each delivered lot's page for the county's own table |
| `maxLots` | hard cap on rows delivered — and therefore charged. `0` = no cap |
| `concurrency` | 1–16, default 8 |
| `proxyConfiguration` | optional; datacenter by default |

The state and county filters read the site's own index pages, which already carry every
storefront's county, state, sale type and date window. Narrowing makes the run **cheaper**, not just
smaller: the prefilled demo (Louisiana sheriff sales, 25 rows) issues 10 requests, where an
unfiltered full-site walk issues 235.

A state code that is not a US state **fails the run before it charges you**, rather than returning a
convincing empty result.

***

### Pricing

**$0.004 per lot delivered — $4.00 per 1,000. No run-start fee.**

You are charged once per row that reaches your dataset, at the same price whether or not per-parcel
enrichment is on. You are **not** charged for:

- lots excluded by any filter or by `maxLots`
- a storefront that exists but currently lists no lots (7 of the 83 on 2026-09-18 — they are listed
  by name in `RUN_SUMMARY`)
- a storefront that could not be read after retries
- a run that matches nothing

***

### Speed and reliability, measured

- **Whole site, no filters:** 235 requests → 15,029 lots, 37.1 s wall clock through the Apify
  datacenter proxy, 14.79 MB on the wire, peak RSS 248 MB.
- **Per-parcel enrichment:** 12.4 requests/second measured over 92 pages through the Apify
  residential proxy, 92 / 92 HTTP 200.
- **No anti-bot anywhere on the data path.** The Apify datacenter and residential pools returned
  byte-identical HTTP 200s to a home IP on every surface. ~700 requests in a day, zero blocks, zero
  429s. No login and no CSRF token is needed for anything this Actor reads.
- **Transport failures are retried on a fresh proxy session**, never mistaken for the end of the
  data. During the full-site run three requests failed transiently (a socket hang-up, a proxy
  `590 UPSTREAM502`, a TLS disconnect); all three succeeded on retry and the run still delivered
  15,029 of 15,029 declared rows. An HTTP 200 carrying an anti-bot interstitial is treated as a
  failure, not as data.
- **Short reads abort the run.** Both large-body surfaces are gzipped — one of them
  `Transfer-Encoding: chunked` with no `Content-Length` at all — so the header cannot be trusted to
  detect truncation. The site's own declared counts are used instead.

***

### Source and legal

Bid4Assets is the auction platform US county tax collectors and sheriffs use to publish statutory
sale notices. The underlying notices are public by law: California Rev. & Tax. Code §§ 3691–3702,
the Pennsylvania Real Estate Tax Sale Law (72 P.S. § 5860.601 *et seq.*) for upset, judicial and
repository sales, and the equivalent state sheriff-sale statutes. The named seller on every
storefront is the county office itself.

**bid4assets.com serves no `robots.txt`** — `/robots.txt` returns 404 (an HTML soft-404 of 31,807
bytes), and there is no `/sitemap.xml`. There is therefore no Disallow line to quote here; that is
stated plainly rather than implied as permission. This Actor reads only the public pages and the
public JSON endpoints those pages call, unauthenticated, exactly as a visitor's browser does. It
does not log in, does not bid, and does not touch the one path that requires an account (the
storefront's "Download Property List Spreadsheet" button, which is a convenience export of rows the
open API already returns).

This Actor is independent and not affiliated with or endorsed by Bid4Assets.

***

### Honest limits

- **Bid4Assets is not every US county.** It is a rotating set — 55 counties in 8 states today. A
  county that is not currently running a sale on the platform is not in the data, and no filter will
  conjure it. `RUN_SUMMARY` and the run log list exactly which counties were available.
- **Tax-sale storefront slugs are dated** (`AlamedaMar26`, `MonroePATaxOct26`) and turn over every
  sale season. Pinning a run with `storefronts` will break eventually; `states` and `counties` will
  not.
- **City and street address are not on the tax-sale API at all.** `locatedCity` and `locatedState`
  are filled on 0 of 14,304 rows. Situs address and the county's assessment only exist on the lot's
  own page, which is why `includeParcelDetails` costs one request per lot. County and state are
  still 100% filled without it.
- **Assessed values are county-optional.** 58.7% total assessed, 34.8% land, 27.2% improvements
  over 92 pages. Legal description 54.3%, acreage 44.6%. Those are the measured rates, not a
  promise, and they vary a lot by county.
- **`parcelId` is null on 194 of 15,029 lots (1.3%) without enrichment.** 7 of those are tax lots
  whose title is a multi-parcel bundle listing two or three APNs joined by "&" or "," — `null`
  rather than silently returning only the first one. (With `includeParcelDetails` on, the county's
  own table lists the bundle in full: `"12220-50-10201 & 30-10320"`. That is also why the detail
  value overrides the title-derived one on 4 of 92 sampled tax pages — every disagreement is a
  truncated bundle, and the detail page is the correct one.) The other 187 are sheriff lots; the
  detail page recovers only a couple more, because those counties genuinely publish no APN.
- **30 sheriff lots publish no opening bid** because the plaintiff's attorney has not filed one.
  `minimumBid` and `currentBid` are `null` on those, never `0`; `attorneyHasSetMinimumBid` tells
  you which.
- **Timestamps are the site's own naive local times**, kept verbatim rather than being given an
  invented timezone. `timeLeftSeconds` is computed against the site's stated Eastern wall clock.
- **The storefront's marketing blurb is not a coverage target.** Of the 24 tax storefronts that
  advertise a parcel count, only 18 match what the API returns — withdrawn lots are still returned.
  The Actor floor-asserts against the API's own `total`, which matched on 47 of 47 storefronts.
- **No contact data.** There is no per-lot phone or email anywhere on the site; the seller *is* the
  named county office, which is what `sellerName` carries. The two mailto addresses on the site are
  corporate.
- **Historical depth is what the site keeps.** 6,614 of today's lots are sold or closed. This is a
  live mirror of the platform, not an archive: once a county removes a storefront, those lots are
  gone from the source.

# Actor input Schema

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

Which surfaces to walk. Empty means both.

## `states` (type: `array`):

Two-letter state codes, e.g. "PA", "CA", "NV". Measured lot counts on 2026-09-18: PA 13,054 - CA 1,353 - NV 420 - LA 113 - WA 70 - OK 10 - WI 6 - FL 3. A code that is not a US state fails the run before it charges you, rather than returning a convincing empty result. Leave empty for every state.

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

County or parish names, matched case-insensitively with or without the "County"/"Parish" suffix, e.g. "Monroe", "Schuylkill County", "East Baton Rouge Parish". Biggest today: Monroe County PA 5,366 lots - Fayette County PA 2,864 - Schuylkill County PA 1,632 - Berks County PA 1,525 - Columbia County PA 722. OR-ed together; combined with the state filter using AND.

## `storefronts` (type: `array`):

Pin the run to named sales. Accepts a slug ("AlamedaMar26", "SchuylkillSheriffSales"), a path ("/storefront/AlamedaMar26") or a full Bid4Assets URL. Tax-sale slugs are dated and turn over every sale season, so a slug that worked last quarter may be gone; the state and county filters above are the durable way to pin a run.

## `auctionStatus` (type: `array`):

Leave empty for every status.

## `excludeWithdrawn` (type: `boolean`):

A county pulls a parcel when the owner pays the back taxes before the sale. Bid4Assets has no status code for it - it prefixes the lot's own title with "***Withdrawn***" and otherwise leaves the row looking live. Measured: 5,760 of 15,029 lots (38.3%) are withdrawn. This Actor detects them, strips the marker out of `title` and flags them in `isWithdrawn`; turn this on to drop them entirely so you never pay for a parcel that is no longer for sale.

## `closingAfter` (type: `string`):

YYYY-MM-DD. Filters on the lot's sale date. Use today's date to get only sales still ahead of you.

## `closingBefore` (type: `string`):

YYYY-MM-DD. Combined with the field above to take one sale season, or the last 12 months of comps.

## `minMinimumBid` (type: `integer`):

Drop lots whose opening bid is below this. Measured across the 14,999 lots that publish a non-zero opening bid: lowest $1, median $1,385, highest $2,447,008.

## `maxMinimumBid` (type: `integer`):

Drop lots whose opening bid is above this.

## `includeParcelDetails` (type: `boolean`):

TAX LOTS, counted over 92 real detail pages (two from each of the 47 tax storefronts): adds the county's own Item Specifics table - parcel number 100% (vs 99.9% from the lot title, and the table also lists multi-parcel bundles in full), situs address 100%, city 86%, legal description 54%, acreage 45%, assessed value / land / improvements 59% / 35% / 27%, zoning 24%, total tax bill 16%, IRS liens 14%, the county parcel-map and GIS links 55% / 46%, deposit required 65%, bid count 65%, and every due-diligence document attached to the lot (302 documents across the 92 pages). SHERIFF LOTS, counted over 56 real detail pages across all 29 sheriff storefronts: mostly address completion - situs 98%, city 48% to 93%, ZIP 57% to 91%, deposit required 82%, legal description 41%. These are county-optional fields: a county that does not publish an assessment leaves it null, never zero.

## `maxLots` (type: `integer`):

Hard cap on rows delivered, and therefore on rows charged. Rows dropped by the filters above are never delivered and never charged. 0 means no cap: the whole site is 15,029 lots and takes about 70 seconds without detail enrichment. A capped run returns the lots closing soonest first, so a small cap gives you what you can still bid on rather than an arbitrary slice.

## `concurrency` (type: `integer`):

Measured: 30 detail pages at concurrency 8 returned 30/30 HTTP 200 in 2.9 s from a home IP, and 92 pages at concurrency 8 returned 92/92 through the Apify residential proxy at 12.4 requests/second. bid4assets did not rate-limit at any point across roughly 700 requests. Leave at 8 unless you are running several of these at once.

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

Optional. bid4assets.com has no anti-bot on any of the surfaces this Actor reads: measured on 2026-09-18, the Apify datacenter (auto) and RESIDENTIAL pools returned byte-identical HTTP 200s to a home IP on both the storefront pages and the parcel API. Datacenter is the default because it is cheaper and nothing here needs more. The site serves no robots.txt at all - /robots.txt is a 404.

## Actor input object example

```json
{
  "saleTypes": [
    "sheriff-sale"
  ],
  "states": [
    "LA"
  ],
  "counties": [],
  "storefronts": [],
  "auctionStatus": [],
  "excludeWithdrawn": false,
  "includeParcelDetails": false,
  "maxLots": 25,
  "concurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per lot: a county tax-defaulted parcel or a sheriff-sale property. County, state, parcel number, opening bid, current bid, sale date and status on every row; plaintiff, defendant, attorney, court case, debt amount and township on the sheriff-sale rows; assessed value, legal description, acreage, zoning, county parcel-map and GIS links and the attached due-diligence documents when per-parcel enrichment is on.

## `runSummary` (type: `string`):

RUN\_SUMMARY: what the site said was there against what was actually read, storefronts that publish no lots, storefronts unreachable after retries, any collection that came back short of its own declared total, the measured field fill for this run, and the billing reconciliation (rows delivered == rows charged).

# 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 = {
    "saleTypes": [
        "sheriff-sale"
    ],
    "states": [
        "LA"
    ],
    "includeParcelDetails": false,
    "maxLots": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/bid4assets-tax-sale-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 = {
    "saleTypes": ["sheriff-sale"],
    "states": ["LA"],
    "includeParcelDetails": False,
    "maxLots": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/bid4assets-tax-sale-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 '{
  "saleTypes": [
    "sheriff-sale"
  ],
  "states": [
    "LA"
  ],
  "includeParcelDetails": false,
  "maxLots": 25
}' |
apify call scrapersdelight/bid4assets-tax-sale-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/bid4assets-tax-sale-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/es2BGIvp4D4VabWYc/builds/PBDg7jR2yXLxKW8Pt/openapi.json
