Skip to content
Developers · REST API

Property Data API

191.3M US property records. RESTful JSON API with parcel search, owner intelligence, permits, deeds, and market analytics.

PropRaven is one parcel dataset delivered three ways — this REST API, a Snowflake Marketplace share (in review), and bulk Parquet and CSV delivery. Field availability and release timing can differ between those channels. The API is the right door when you need a parcel, an owner or a cohort inside an application: one HTTPS call returns the owner of record, assessed value, last sale, permits, deed history and hazard risk wherever the county publishes them, with an official client in Python, TypeScript and Go and a hosted Model Context Protocol server for AI agents.

What the API returns

The v1 surface is organised into the endpoint groups below — the tags of the published OpenAPI 3.1 specification (version 1.2.1, which describes 87 paths and 92 operations). Every response is JSON, and every parcel is addressed by its canonical state_fips:county_fips:parcel_id identifier (for example 37:119:11906402). The parcel build currently served is v=2026-10-12.

GroupWhat it returns
ParcelsOne parcel by ID, plus its owner, permits, deed history, property-tax delinquency status, risk assessment, traffic history, GeoJSON polygons for a bounding box, and the paid dossier / comp pack / risk score.
SearchParcel search by bounds, filters and sorting; full paginated text + attribute search; address / place / parcel autocomplete; licensed-firm search in a place; CSV export of a result set.
CoverageCoverage statistics — where PropRaven has parcel data, by state and county.
DealsAbsentee owners, flips, contractors by permit activity, entity-owned parcels, high land-to-improvement ratio, lender profiles, long-held parcels, portfolio owners, county-quarter transaction summaries.
LeadsA lead feed priced per lead, with a free masked preview of count, price and sample.
MarketCounty market statistics, a single-county detail view, market trends, and flip activity grouped by county.
OwnersOwner search by name, owner profile, the owner's properties, portfolio summary, recorded deed transactions, and the paid owner intelligence report.
WebhooksCreate, list, inspect and disable webhook endpoints, and read recent delivery attempts.
AccountCurrent-period usage and quota for your key.
StorefrontThe sealed field catalog and try-before-buy availability quotes — the free discovery surface agents read first.
Property IntelligenceEvidence-backed property signals for a parcel, plus the runs, named scenarios and investigation handoffs your key owns.
CreditsFund and read a prepaid credit balance over x402.
WatchCreate a watch on a parcel or cohort (free) and poll it for new changes.
VerifyVerify facts for one parcel, or in batch, with a free preview.

Getting Started

  1. 1Create an account at propraven.com/sign-up
  2. 2Generate an API key at Settings > API Keys
  3. 3Make your first request (see examples below)

The Free plan is $0 with 1,000 lookups a month and no card required. New accounts land on /developers/welcome with the key on screen.

Authentication

Include your API key in the Authorization header:

Authorization: Bearer pz_your_key_here

Keys are prefixed pz_ and are the only credential the API uses — there is no OAuth dance for server-to-server calls, and the same key authenticates the SDKs and the hosted MCP server. A key is required on every endpoint except the free discovery surface (coverage statistics, the storefront catalog and availability quotes, and lead-feed previews) and the x402-paid resources, which accept a signed wallet payment instead of a key. Being authenticated is not the same as being entitled: a free-tier key asking for a paid dossier gets the same HTTP 402 an anonymous caller does.

Keys are created and revoked self-serve under Settings > API Keys. Treat them as secrets — never ship one in a browser bundle. For the supply-chain side (SLSA provenance on every SDK and MCP release, OIDC trusted publishing) see Security.

Code Examples

The raw HTTP surface needs nothing but a key. The single-parcel lookup below is the first call most integrations make; the ID is the state FIPS, county FIPS and assessor parcel number.

curl

curl -H "Authorization: Bearer pz_your_key" \
  "https://propraven.com/api/v1/parcels/37:119:11906402"

Python (requests)

import requests

resp = requests.get(
    "https://propraven.com/api/v1/parcels/37:119:11906402",
    headers={"Authorization": "Bearer pz_your_key"}
)
parcel = resp.json()
print(parcel["address"], parcel["total_assessed_value"])

JavaScript (fetch)

const res = await fetch(
  "https://propraven.com/api/v1/parcels/37:119:11906402",
  { headers: { Authorization: "Bearer pz_your_key" } }
);
const parcel = await res.json();
console.log(parcel.address, parcel.total_assessed_value);

Official SDKs: Python, TypeScript, Go

All three clients are generated from the same OpenAPI 3.1 document (/openapi.json) by generators that live in each SDK repo, and a scheduled job opens a regeneration pull request whenever the spec changes. Method names come from the spec's x-sdk-group / x-sdk-method (client.parcels.get is GET /api/v1/parcels/{id}). Every client reads PROPRAVEN_API_KEY from the environment, retries 429, 503 and 504 with backoff (honouring Retry-After, never retrying a 402), raises a typed error built from the problem body, iterates paginated lists for you, and verifies webhook signatures. npm and PyPI releases are published from GitHub Actions with trusted publishing and carry provenance. Source: jdw2111/propraven-python, jdw2111/propraven-typescript and jdw2111/propraven-go.

Python — propraven on PyPI · full guide

pip install propraven
from propraven import PropRaven

client = PropRaven()  # reads PROPRAVEN_API_KEY from env

parcel = client.parcels.get("37:119:12104406")
print(parcel["owner_name"], parcel["total_assessed_value"])

for row in client.deals.absentee_iter(county_fips="37119", max_items=500):
    print(row["parcel_id"], row["total_assessed_value"])

Python 3.9+. Responses are plain dicts with TypedDict hints; AsyncPropRaven is the asyncio client.

TypeScript — @propraven/sdk on npm · full guide

npm install @propraven/sdk
import { PropRaven } from "@propraven/sdk";

const client = new PropRaven(); // reads PROPRAVEN_API_KEY from env

const parcel = await client.parcels.get("37:119:12104406");
console.log(parcel.owner_name, parcel.total_assessed_value);

for await (const row of client.deals.absenteeAll({ county_fips: "37119" }, { maxItems: 500 })) {
  console.log(row.parcel_id, row.total_assessed_value);
}

Node 18+, zero dependencies, ESM + CJS. Server-side only: the REST API sends no CORS headers and keys are secret, so the client refuses to start in a browser.

Go — github.com/jdw2111/propraven-go · full guide

go get github.com/jdw2111/propraven-go@latest
package main

import (
    "context"
    "fmt"
    "log"

    propraven "github.com/jdw2111/propraven-go"
)

func main() {
    client := propraven.NewClient() // reads PROPRAVEN_API_KEY from env
    ctx := context.Background()

    parcel, err := client.Parcels.Get(ctx, "37:119:12104406", nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(*parcel.OwnerName, *parcel.TotalAssessedValue)
}

Go 1.22+, standard library only. propraven.IsNotFound(err), IsRateLimited and friends classify errors.

Rate limits and pricing

Plans are metered on lookups per month; that monthly cap is the enforced meter, and a per-key burst limit sits on top of it. The four tiers below are the same ladder the pricing page publishes.

TierLookupsPriceIncludes
Free1,000 / mo$0Full records in the web app, self-serve API keys. No card required.
Developer100,000 / mo$99/mo · 14-day free trialFull API, SDKs & webhooks, MCP connector, owner & sponsor graph.
Team1,000,000 / mo$1,000/moEverything in Developer, bulk export, priority support.
Data licenseUnmetered (bulk)CustomFull state or national tables via Snowflake, Databricks or Parquet. Annual license, invoiced.

Unauthenticated responses and every 429 carry the rate-limit headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Read your current-period consumption from GET /api/v1/account/usage. Sustained throughput above the Team cap, or whole-state and national tables, is the data license path rather than a bigger meter.

For AI agents: MCP server and x402 pay-per-call

The API has a second front door built for software that reasons rather than renders. The hosted Model Context Protocol server at https://propraven.com/mcp exposes 47 typed tools — parcel lookup, search and compare, owner pierce, hazard score, valuation estimate, permit and sales history, the deal finders, and the storefront pair — to Claude Desktop, Claude Code, Cursor, Grok, ChatGPT and any MCP-compatible client. The same pz_ key authenticates it as a bearer header; connector UIs such as Claude and Grok sign in with OAuth instead (rotating refresh tokens, no client id to enter). A local @propraven/mcp package on npm speaks stdio for latency-sensitive work. Setup for each client is on the MCP page, which is generated from the live tool registry.

{
  "mcpServers": {
    "propraven": {
      "url": "https://propraven.com/mcp",
      "headers": { "Authorization": "Bearer pz_your_real_key_here" }
    }
  }
}

Agents that arrive with a wallet instead of an account use x402. The flow is discover → preview → pay: 3 tools (get_catalog and check_availability) are free and return the sealed field catalog with measured per-state coverage and the exact price of a specific parcel's dossier before anything is spent. Requesting the paid resource without payment returns HTTP 402 with the precise USDC-on-Base amount to sign; sign an EIP-3009 transferWithAuthorization, retry with the X-PAYMENT header, and the 200 carries the dossier plus an on-chain settlement receipt. A failed build is never charged. The dossier price is value-tiered and auditable — clamp($5 × V × R × F, $2, $20) — and lead feeds, owner reports, comp packs and risk scores follow the same 402 contract (owner reports, like every owner-name endpoint, also require your API key: a wallet alone is refused). The full walkthrough is on Connect your agent.

Data coverage behind the API

191.3M parcels · 110.0M mapped, counted on the 2026-08-30 map build, across 51 states (all 50 plus DC), described by 620 catalogued columns (44 rebuilt every epoch and 576 carried from an April 2026 reference export; 56 of the 620 are withheld from sale while they are rebuilt). The parcel build currently served is v=2026-10-12; per-state releases in the app database can lag it. The collected facts include 157M+ permit records, 10M+ title transfers, 27M+ resolved owners and 229M+ parcel records with a site address. 110.0M distinct locations carry a coordinate the map can draw. Coverage is uneven by design of the sources — county assessors, recorders and permit offices publish different fields — so check GET /api/v1/coverage or the storefront catalog for the jurisdiction you care about rather than trusting a national average. The data page publishes live counts, the schema explorer and per-field fill rates; parcel data explains what a parcel record contains.

Representative endpoints

Twelve of the 92 operations in the spec, as a map of the territory. Paths are relative to https://propraven.com.

MethodPathDescriptionAuth
GET/api/v1/parcels/{id}Get parcel by IDAPI key
GET/api/v1/parcels/{id}/ownerGet parcel owner details and portfolioAPI key
GET/api/v1/parcels/{id}/permitsGet parcel permitsAPI key
GET/api/v1/parcels/{id}/deedsGet parcel deed historyAPI key
POST/api/v1/searchSearch parcels by bounds, filters, and sortingAPI key
GET/api/v1/search/exportExport search results as CSVAPI key
GET/api/v1/coverageGet coverage statisticsKey optional
GET/api/v1/deals/absenteeFind absentee ownersAPI key
GET/api/v1/owners/{name}/portfolioGet owner portfolio summaryAPI key
GET/api/v1/storefront/catalogMachine Storefront — sealed field catalogNone
GET/api/v1/parcels/{id}/reportParcel dossier (paid, provenance-first)Key or x402
GET/api/v1/account/usageCurrent-period usage and quotaAPI key

Webhooks

Polling is the wrong shape for change detection across 191.3M parcels, so the API pushes. Register an endpoint with POST /api/v1/webhooks and PropRaven delivers parcel.sold, parcel.permit_filed and parcel.owner_changed events signed with HMAC-SHA256 in the X-PropRaven-Signature header; each event carries a deterministic event_id for idempotency. Verify each delivery with a constant-time HMAC check and a five-minute replay window — plain Node.js below, plain Python on the webhooks page, or the helper every SDK ships (TypeScript verifyWebhook, Python verify_webhook, Go VerifyWebhook, shown below).

import { createHmac, timingSafeEqual } from "node:crypto";

// header:  the X-PropRaven-Signature value, e.g. "t=1715882400000,v1=ab12..."
// rawBody: the exact request body string, before any JSON parsing
// secret:  the whsec_... value returned when you created the endpoint
function verifyPropRavenSignature(header, rawBody, secret, toleranceMs = 300000) {
  const parts = {};
  for (const kv of String(header).split(",")) {
    const i = kv.indexOf("=");
    if (i > 0) parts[kv.slice(0, i).trim()] = kv.slice(i + 1).trim();
  }
  const t = Number(parts.t);
  if (!Number.isFinite(t) || !parts.v1) return false;
  if (Math.abs(Date.now() - t) > toleranceMs) return false; // replay window
  const expected = createHmac("sha256", secret).update(parts.t + "." + rawBody).digest();
  const given = Buffer.from(parts.v1, "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}
import (
    "io"
    "net/http"
    "os"

    propraven "github.com/jdw2111/propraven-go"
)

func handler(w http.ResponseWriter, r *http.Request) {
    rawBody, _ := io.ReadAll(r.Body) // the exact request body, before any JSON parsing
    event, err := propraven.VerifyWebhook(rawBody,
        r.Header.Get("X-PropRaven-Signature"), os.Getenv("PROPRAVEN_WEBHOOK_SECRET"))
    if err != nil {
        http.Error(w, "invalid signature", http.StatusUnauthorized)
        return
    }
    _ = event // json.RawMessage: unmarshal into your own struct; the name is in event_type
    w.WriteHeader(http.StatusNoContent)
}

Error Handling

Every error is an RFC 7807 problem (application/problem+json) with a stable code. A bad parameter is a 400 invalid_parameter that names each one in errors; error bodies never contain database text:

{
  "type": "https://api.propraven.com/errors/invalid-parameter",
  "title": "Bad Request",
  "status": 400,
  "detail": "limit: must be >= 1",
  "code": "invalid_parameter",
  "errors": [{ "param": "limit", "message": "must be >= 1" }],
  "request_id": "iad1::abc123"
}
StatusMeaning
400Bad request (missing or invalid parameters)
401Invalid or missing API key
402Payment required — the body is an x402 offer naming the exact amount
403API key is inactive or expired
404Resource not found
429Rate limit exceeded (check Retry-After header)
405Method not allowed (the Allow header lists the methods)
500Internal server error
503 / 504Source temporarily unavailable, or the query ran out of time (query_timeout); retry after Retry-After

Pagination and numbers

List endpoints take limit and offset and return {data, total, limit, offset}: ask for the next page with offset + limit until a page comes back shorter than limit. GET /api/v1/search/full uses a cursor instead: pass the previous page's nextCursor as after until it is null. Since API 1.2.0, every field the spec types as a number is a JSON number (values such as total_assessed_value used to arrive as strings like "19388200.00"); identifiers such as parcel_id, county_fips and zip stay strings.

OpenAPI Specification

The full OpenAPI specification is available at /openapi.json(OpenAPI 3.1, version 1.2.1, 92 operations, validated in CI against recorded production responses). Import it into Insomnia or any OpenAPI-compatible tool, load the generated Postman collection, or point a ChatGPT Custom GPT Action at it with Bearer authentication.

Frequently asked questions

Is there a free tier for the property data API?
Yes. The Free plan is $0 and includes 1,000 lookups a month with self-serve API keys, and no card is required. The Developer plan ($99/mo, 100,000 lookups) has a 14-day free trial.
How do I authenticate requests?
Create a pz_ API key under Settings > API Keys and send it as a bearer token: Authorization: Bearer pz_your_key. The same key works for the REST API, the SDKs and the hosted MCP server.
Which SDKs are available?
Official clients for Python (pip install propraven), TypeScript (npm install @propraven/sdk) and Go (go get github.com/jdw2111/propraven-go), all generated from the same OpenAPI 3.1 spec and published with SLSA provenance.
Can an AI agent use the API without an account?
Partly. The parcel dossier, comp pack and risk score accept x402 pay-per-call without an account: request without payment, receive an HTTP 402 naming the exact USDC-on-Base amount, sign it and retry. Owner names and mailing addresses are withheld without an account, and owner reports and lead feeds require a PropRaven account (an API key or MCP sign-in). The catalog and availability preview are free and need no key.
How much data is behind the API?
191.3M parcels · 110.0M mapped (counted on the 2026-08-30 map build) across 51 states, described by 620 catalogued columns (44 rebuilt every epoch and 576 carried from an April 2026 reference export; 56 of the 620 are withheld from sale while they are rebuilt). Coverage varies by state and field, so check /api/v1/coverage for the jurisdictions you need.