Property Data API checking…

Austin + DFW property screening data — search, map filters, facets, exports and annotations.
Base URL: https://179-236-232-116.sslip.io/api · Interactive spec: /api/docs

…properties in database
…county rolls linked
…filterable columns

Quick start

Paste into a browser:

# view the first 100 properties as JSON
https://179-236-232-116.sslip.io/api/v1/properties?limit=100

# download EVERYTHING as CSV (77k rows, all columns)
https://179-236-232-116.sslip.io/api/v1/export.csv

# the full field catalog (what you can filter on)
https://179-236-232-116.sslip.io/api/v1/schema

Or search with filters (works from anywhere):

curl -s -X POST https://179-236-232-116.sslip.io/api/v1/search \
  -H 'content-type: application/json' -d '{
    "area": {"type": "bbox", "bbox": [-96.95, 33.05, -96.60, 33.30]},
    "filters": {"beds": {"gte": 3}, "price": {"lte": 600000}},
    "sort": {"field": "price", "dir": "asc"},
    "limit": 10
  }'

Endpoints

EndpointWhat it does
GET/healthzService alive check
GET/v1/schemaThe live catalog: every filterable field, operators, expression language, limits
POST/v1/searchThe workhorse: area + filters + sort + paging → property rows
POST/v1/aggregateFacet counts for filter UIs (price histogram, group-bys)
POST/v1/clustersMap pins: grid cells with counts (zoom-dependent)
GET/v1/propertiesSimple paged list of all properties (browser friendly)
GET/v1/properties/{zpid}One property: full row + county values + tags + notes
GET/v1/export.csv · /v1/export.jsonlDownload everything (or ?limit=N)
POST/v1/exportSame as above but with filters in the body
POST GET PATCH DELETE /v1/notesNotes on properties (/v1/notes?zpid=… to list)
PUT DELETE GET /v1/properties/{zpid}/tags/{tag}Attach / remove / list tags (PUT is idempotent)
GET/v1/tagsAll tags with usage counts
POST GET PATCH DELETE /v1/saved_searchesSave / list / edit / delete filter combinations

Search body anatomy

{
  "area":    { "type": "bbox", "bbox": [minLng, minLat, maxLng, maxLat] },   // optional
  "filters": { "beds": {"gte": 3}, "price": {"between": [300000, 600000]} }, // optional
  "sort":    { "field": "price", "dir": "asc" },   // any field in /v1/schema
  "limit":   50,        // max 200
  "offset":  0,         // max 50000
  "count":   true       // include "total" (adds a count query)
}

Area types: bbox · polygon (GeoJSON, draw-your-own) · region () · radius ({"lat":33.2,"lng":-96.8,"meters":5000})

Filters — the complete catalog

This section renders live from /v1/schema, so it is always current.

Numeric fields

Operators:

{"price": {"gte": 300000, "lte": 600000}}        // range
{"beds": {"gte": 3}}                              // at least
{"year_built": {"between": [2010, 2025]}}         // inclusive
{"zestimate": {"exists": true}}                   // has a value
{"baths": {"in": [2, 2.5, 3]}}                    // any of

Dates

{"enriched_at": {"gte": "2026-09-01"}}            // since Sep 1

Text fields

Operators:

{"status": {"in": ["Active", "Pending"]}}
{"home_type": "SINGLE_FAMILY"}
{"agent": {"contains": "smith"}}                  // case-insensitive substring
{"status": {"ne": "Sold"}}                        // not equal

Boolean & special

{"enriched": true}                                 // only deeply-enriched records
{"county_matched": true}                           // has county appraisal data
{"zip": {"in": ["75034", "75035"]}}
{"county": {"in": ["collin", "denton", "travis"]}}
{"county_market_value": {"gte": 400000}}           // unified county market value
{"data": {"challenge": true}}                      // raw JSONB containment (any scraped field)
{"text": "oak street"}                             // free text on address + description
{"schools_text": "Frisco", "school_rating": {"gte": 8}}

Any county column (all columns of the county rolls)

{"county_col": {"field": "currvalassessed", "county": "collin", "gte": 500000}}
{"county_col": {"field": "ownermarketvalue", "gte": 400000}}   // county auto-detected
{"county_col": {"field": "imprvActualYearBuilt", "gte": 2010}} // denton: year built

OR groups

{"$or": [
  {"beds": {"gte": 4}, "price": {"lte": 400000}},
  {"area_sqft": {"gte": 3000}, "year_built": {"gte": 2015}}
]}

Expressions — conditions across multiple fields

When a filter depends on other columns (price vs county value, price per sqft, conditional thresholds), use expr: a safe JSON expression tree (no raw SQL, everything validated).

Columns available:

Accessors:

Operators:

// price per sqft under $200
{"expr": ["<", ["/", ["col","price"], ["col","area_sqft"]], 200]}

// below-market: asking price under 80% of county market value
{"county_matched": true,
 "expr": ["<", ["col","price"], ["*", ["col","county_market_value"], 0.8]]}

// conditional thresholds: newer homes must clear a higher price bar
{"expr": ["if", [">", ["col","year_built"], 2015],
                 [">", ["col","price"], 300000],
                 [">", ["col","price"], 200000]]}

// multiple expressions are ANDed
{"expr": [ ["notnull", ["col","zestimate"]],
           ["<", ["col","price"], ["col","zestimate"]] ]}

// any county column in an expression
{"expr": [">", ["col","county.collin.currvalassessed"], 500000]}

Recipes

Map crop → results

{"area": {"type": "polygon", "geojson": {…}},
 "filters": {"beds": {"gte": 3}},
 "limit": 50}

Zoomed-out map pins

POST /v1/clusters
{"area": {"type": "bbox", …},
 "max_cells": 200}

Filter chips / counts

POST /v1/aggregate
{"area": {…},
 "aggs": {"price_histogram": true,
          "group_by": ["status","region"]}}

Page through everything

GET /v1/properties?limit=1000&offset=0
… repeat with offset += 1000

Download all data

GET /v1/export.csv
GET /v1/export.jsonl

Save a filter combo

POST /v1/saved_searches
{"name": "frisco 3bd",
 "body": {"filters": {"beds": {"gte": 3}}},
 "owner": "das"}

Annotate a property

POST /v1/notes {"zpid": "…", "body": "check flood zone"}
PUT  /v1/properties/{zpid}/tags/hot

One property, everything

GET /v1/properties/102956128
→ row + county values + tags + notes

Limits & errors

RuleValue
Rate limit10 requests/second per IP (burst 30)
Search page sizelimit ≤ 200 · offset ≤ 50000
List page sizelimit ≤ 1000
Export≤ 200,000 rows per request (CSV or JSONL)
AuthNone yet — testing phase. Treat URLs as team-internal.

Errors come back as {"detail": "…"} with HTTP 400 (bad filter/field/operator), 404 (not found), 405 (wrong method), 429/503 (rate limit).

Try it

Runs a real search against the live database.

— response appears here —

Property Data API · served from the company VPS · guide source: /srv/team/property-data/api/guide.html
Everything on this page is live: fields, counts and the playground all read from the running API.