# Look up a public record

Source: https://help.ruuster.com/api/public-records-lookup

```http
GET https://api.ruuster.com/v1/public-records/lookup
```

One US home's county record by address, parcel number or propertyId, whether or not it was ever on the MLS: parcel, building facts, tax and assessed value, AVM, last sale and the loans still open, plus owner names and mailing address with include=owner. A lookup call, one home at a time.

- Authentication: `x-api-key` and `x-email` headers of an owner or admin
- Rate limit: *lookup* call, 20 per minute per key, within the 120 per minute
- Counts toward your brokerage's daily public record allowance (500 by default)

Finds one home in the county public records Ruuster licenses, in the US states your brokerage's MLSs cover (a home elsewhere returns `400 mls_not_in_scope`, which doesn't count toward the allowance). It works for homes that have never been listed, which is most of the homes your clients own, and for any listing in your MLSs by its `propertyId`. To see every recorded deed and mortgage on the home, pass the `recordId` from here to [Get a home's deed and mortgage history](https://help.ruuster.com/api/public-records-history). To value a home with no MLS record, send the same `recordId` as `subject.publicRecordId` to [Run a CMA](https://help.ruuster.com/api/cma-create).

This is a *lookup* call (20 per minute) and counts toward your brokerage's daily public record allowance (500 per day by default, shared with the history call). It answers one home per call: it's for looking up your clients' and leads' homes as you work them, not for building lists of homes or owners, which Ruuster's data license doesn't allow.

Send exactly one of `address`, `parcelNumber` (with `postalCode`) or `propertyId`. An address needs the house number, the street and the 5-digit zip; a unit (`#4B`, `Unit 4B`, `Apt 4B`) at the end of the street line picks one unit in a building. A parcel number is matched within its zip, ignoring dashes, dots and spaces.

County records arrive in periodic loads and run about two months behind recording, so a recent sale or refinance may not show yet; `record.asOf` is the date of the load. They aren't a title, vesting or encumbrance report. `record.loans` counts only the loans still open: a loan whose release was recorded, or that a later refinance paid off, is left out even when the county still lists it.

## Query parameters

| Name | Type | Default | Description |
|---|---|---|---|
| `address` | string | | The home's street address with its 5-digit zip, for example `6104 Oak Forest Rd, Edmond, OK 73025` |
| `parcelNumber` | string | | Instead of `address`: the county parcel number (APN). Needs `postalCode` |
| `postalCode` | string | | With `parcelNumber`: the home's 5-digit zip |
| `propertyId` | string | | Instead of `address`: a listing's `propertyId` in one of your MLSs. Its address, and its parcel number when the MLS has one, find the county record |
| `include` | string | | `owner` adds `record.owner`: the owners' names, their mailing address and whether they live there |

## Response

`data.found` is `false`, with `data.record` `null`, when the county has no record that matches; that's still a `200` and still counts toward the daily allowance. `data.resolvedBy` says which parameter found it, and `data.propertyId` echoes the listing when you sent one.

`data.record`:

| Field | Description |
|---|---|
| `recordId` | The county record's id: pass it to the history call or to a CMA as `subject.publicRecordId` |
| `parcelNumber`, `address`, `unit`, `city`, `state`, `postalCode`, `lat`, `lng` | Where the home is |
| `propertySubType`, `bedrooms`, `bathrooms`, `livingArea`, `lotSizeAcres`, `yearBuilt`, `yearBuiltEffective`, `stories`, `garageSpaces`, `pool`, `basement`, `condition` | Building facts as the assessor records them |
| `tax` | `year`, `annualAmount`, `assessedYear`, `assessedValue`, `marketValue` |
| `lastSale` | The last recorded transfer: `price` (null when the county doesn't disclose prices), `recordingDate`, `documentType` |
| `occupancy` | `owner` when the owner's mailing address is the home, `absentee` otherwise |
| `loans` | `totalOriginal`, `estimatedBalance` and the open `first` and `second` liens |
| `avm` | Automated valuation: `value`, `low`, `high`, `confidence` (0 to 100), `asOf` |
| `asOf` | When the county record was loaded |
| `owner` | Only with `include=owner`: `names` (people, or a trust or company's name), `mailingAddress`, `careOf`, `occupancy` |

`meta.notice` repeats the limits of the data. Owner details may not be used to contact anyone who has asked not to be solicited.

## Example request

cURL:

```bash
curl -s "https://api.ruuster.com/v1/public-records/lookup?address=6104%20Oak%20Forest%20Rd%2C%20Edmond%2C%20OK%2073025" \
  -H "x-api-key: $RUUSTER_API_KEY" \
  -H "x-email: $RUUSTER_EMAIL"
```

Node.js:

```javascript
const res = await fetch("https://api.ruuster.com/v1/public-records/lookup?address=6104%20Oak%20Forest%20Rd%2C%20Edmond%2C%20OK%2073025", {
  headers: {
    "x-api-key": process.env.RUUSTER_API_KEY,
    "x-email": process.env.RUUSTER_EMAIL,
  },
});
const body = await res.json();
if (!body.ok) throw new Error(`${body.error.code}: ${body.error.message} (${body.requestId})`);
console.log(body.data);
```

Python:

```python
import os
import requests

res = requests.get(
    "https://api.ruuster.com/v1/public-records/lookup?address=6104%20Oak%20Forest%20Rd%2C%20Edmond%2C%20OK%2073025",
    headers={"x-api-key": os.environ["RUUSTER_API_KEY"], "x-email": os.environ["RUUSTER_EMAIL"]},
    timeout=30,
)
body = res.json()
if not body["ok"]:
    raise RuntimeError(f'{body["error"]["code"]}: {body["error"]["message"]} ({body.get("requestId")})')
print(body["data"])
```

## Example response

```json
{
  "ok": true,
  "data": {
    "found": true,
    "resolvedBy": "address",
    "propertyId": null,
    "record": {
      "recordId": "400000123456",
      "parcelNumber": "R-1180-22-007",
      "address": "4417 W SADDLE RIDGE DR",
      "unit": null,
      "city": "TUCSON",
      "state": "AZ",
      "postalCode": "85742",
      "lat": 32.39214,
      "lng": -111.05873,
      "propertySubType": "Single Family Residence",
      "bedrooms": 4,
      "bathrooms": 3,
      "livingArea": 2486,
      "lotSizeAcres": 0.31,
      "yearBuilt": 2004,
      "yearBuiltEffective": null,
      "stories": 1,
      "garageSpaces": 3,
      "pool": true,
      "basement": null,
      "condition": "Good",
      "tax": { "year": 2025, "annualAmount": 4212, "assessedYear": 2025, "assessedValue": 41860, "marketValue": 418600 },
      "lastSale": { "price": 389000, "recordingDate": "2019-06-14", "documentType": "Warranty Deed" },
      "occupancy": "owner",
      "loans": {
        "totalOriginal": 310000,
        "estimatedBalance": 262480,
        "first": { "amount": 310000, "rate": 2.875, "type": "Conventional", "recordingDate": "2021-02-03", "lenderType": "Bank" },
        "second": null
      },
      "avm": { "value": 548000, "low": 512000, "high": 581000, "confidence": 89, "asOf": "2026-09-01" },
      "asOf": "2026-10-03",
      "owner": {
        "names": ["JANE Q SAMPLE", "JOHN R SAMPLE"],
        "mailingAddress": { "line1": "4417 W SADDLE RIDGE DR", "unit": null, "city": "TUCSON", "state": "AZ", "postalCode": "85742" },
        "careOf": null,
        "occupancy": "owner"
      }
    }
  },
  "meta": {
    "brokerageId": "4f6c2a9e-8d31-4b57-9a0e-2c7d5e1b8f43",
    "ms": 214,
    "notice": "From county public records, which run about two months behind recording. Not a title, vesting or encumbrance report; mortgage status is estimated from recorded releases, later sales and refinances. Owner details may not be used to contact anyone who has asked not to be solicited."
  }
}
```
