← Back to Settings

UglyHouses.ai REST API

Programmatically score properties, retrieve leads, and enrich owner data. Available to Business and Founding Member plans.

Base URL: https://app.uglyhouses.ai/api/v1

Authentication

All API requests require a Bearer token in the Authorization header. Generate your API key from Settings.

curl https://app.uglyhouses.ai/api/v1/properties \
  -H "Authorization: Bearer uh_live_your_api_key_here"

API keys start with uh_live_. Keep your key secret -- do not expose it in client-side code.

Error Responses

All errors return a JSON object with an error field.

{
  "error": "Invalid API key"
}

401 -- Invalid or missing API key

402 -- Plan limit reached

403 -- Plan does not include API access

404 -- Resource not found

422 -- Skip trace failed (no data available)

500 -- Internal server error

Endpoints

GET/api/v1/properties

List your scored properties (leads), sorted by distress score descending.

Query Parameters

minScoreinteger (1-10, default: 6) -- Minimum distress score
limitinteger (1-500, default: 50) -- Results per page
offsetinteger (default: 0) -- Pagination offset
curl "https://app.uglyhouses.ai/api/v1/properties?minScore=7&limit=10" \
  -H "Authorization: Bearer uh_live_your_key"

Response

{
  "data": [
    {
      "id": "uuid",
      "address": "123 Main St",
      "city": "Nashville",
      "state": "TN",
      "zipCode": "37206",
      "distressScore": 8,
      "repairTier": "major",
      "repairEstimate": "$45,000-$65,000",
      "investorNarrative": "Severe roof damage, overgrown yard...",
      "occupancyVerdict": "clearly-vacant",
      "conditionVerdict": "stop-the-car",
      "neighborhoodContrast": "worst-on-good-block",
      "conditionSignals": ["damaged_roof", "overgrown"],
      "confidence": "HIGH",
      "ownerName": "John Smith",
      "streetViewUrl": "https://...",
      "estimatedValue": 185000,
      "estimatedEquity": 148000,
      "equityRatio": 0.8,
      "isAbsenteeOwner": true,
      "taxDelinquent": false,
      "isVacant": true,
      "preForeclosure": false,
      "ownerBankruptcy": false,
      "scoredAt": "2026-04-08T12:00:00Z",
      "enrichedAt": "2026-04-08T12:01:03Z"
    }
  ],
  "total": 42,
  "limit": 10,
  "offset": 0
}

estimatedEquity and equityRatio are the strongest motivation signals in the response — high-equity owners (60%+) can close without a short sale, and the condition of the property is already captured in distressScore. Filter on both for the highest-conviction leads.

POST/api/v1/score

Submit addresses for distress scoring. Max 100 addresses per request. Scoring runs asynchronously -- poll the job endpoint for results.

curl -X POST https://app.uglyhouses.ai/api/v1/score \
  -H "Authorization: Bearer uh_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "addresses": [
      {
        "street": "123 Main St",
        "city": "Nashville",
        "state": "TN",
        "zip": "37206"
      },
      {
        "street": "456 Oak Ave",
        "city": "Nashville",
        "state": "TN",
        "zip": "37210"
      }
    ]
  }'

Response (202)

{
  "jobId": "uuid",
  "addressCount": 2,
  "estimatedMinutes": 1,
  "truncated": false
}

GET/api/v1/jobs/:id

Check status of a scoring job. Once complete, results are included in the response.

curl https://app.uglyhouses.ai/api/v1/jobs/JOB_ID \
  -H "Authorization: Bearer uh_live_your_key"

Response

{
  "id": "uuid",
  "status": "complete",
  "totalAddresses": 2,
  "scored": 2,
  "failed": 0,
  "progress": 100,
  "startedAt": "2026-04-08T12:00:00Z",
  "completedAt": "2026-04-08T12:01:30Z",
  "results": [
    {
      "id": "uuid",
      "address": "123 Main St",
      "city": "Nashville",
      "state": "TN",
      "zipCode": "37206",
      "distressScore": 8,
      "repairTier": "major",
      "repairEstimate": "$45,000-$65,000",
      "investorNarrative": "...",
      "confidence": "high",
      "scoredAt": "2026-04-08T12:01:00Z"
    }
  ]
}

Possible status values: scoring, complete, failed

POST/api/v1/enrich/:propertyId

Skip trace a scored property to get owner name, phone numbers, emails, and mailing address. Consumes 1 skip trace credit from your plan.

curl -X POST https://app.uglyhouses.ai/api/v1/enrich/PROPERTY_ID \
  -H "Authorization: Bearer uh_live_your_key"

Response

{
  "propertyId": "uuid",
  "enriched": true,
  "owner": {
    "name": "John Smith",
    "isAbsentee": true,
    "mailingAddress": "789 Other St, Memphis, TN 38103"
  },
  "phones": [
    {
      "number": "(615) 555-1234",
      "type": "mobile",
      "isDnc": false,
      "isLitigator": false,
      "safe": true
    }
  ],
  "bestPhone": "(615) 555-1234",
  "emails": ["john@example.com"],
  "property": {
    "lastSoldDate": "2018-06-15",
    "lastSoldPrice": 185000
  }
}

GET/api/health

Public uptime probe. No authentication required — wire it into BetterStack, UptimeRobot, or Pingdom to detect outages before your customers do. Returns 200 when healthy, 503 when the database is unreachable.

curl https://app.uglyhouses.ai/api/health

Response

{
  "ok": true,
  "timestamp": "2026-04-11T14:23:07.421Z",
  "service": "uglyhouses.ai",
  "checks": {
    "database": { "ok": true, "latencyMs": 12 }
  }
}

Webhooks

Configure an HTTPS webhook URL under Settings and we will POST event payloads to you as they happen — no polling required. Deliveries are retried automatically with exponential backoff on 5xx responses.

Events

lead.scored — fires when a property finishes AI scoring.
lead.enriched — fires when owner contact data is attached to a property.

Payload Shape

POST https://your-domain.com/your-webhook-path
Content-Type: application/json
User-Agent: UglyHouses-Webhook/1.0
x-uglyhouses-signature: sha256=<hex-digest>

{
  "event": "lead.scored",
  "timestamp": "2026-04-11T14:23:07.421Z",
  "data": {
    "propertyId": "uuid",
    "address": "123 Main St",
    "city": "Nashville",
    "state": "TN",
    "zip": "37206",
    "distressScore": 8,
    "repairTier": "major",
    "repairEstimate": "$45,000-$65,000",
    "investorNarrative": "Severe roof damage, overgrown yard...",
    "condition": "distressed",
    "confidence": "high"
  }
}

Verifying Signatures

Every request includes an x-uglyhouses-signature header with an HMAC-SHA256 digest of the raw request body, keyed with the webhook secret shown in your Settings page. Compute the expected digest and compare in constant time before trusting the payload.

Node.js

import crypto from "node:crypto";

export function verifyUglyhousesSignature(rawBody, header, secret) {
  const provided = (header || "").replace(/^sha256=/, "");
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(provided, "hex");
  const b = Buffer.from(expected, "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Python

import hmac, hashlib

def verify_uglyhouses_signature(raw_body: bytes, header: str, secret: str) -> bool:
    provided = (header or "").removeprefix("sha256=")
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(provided, expected)

PHP

function verify_uglyhouses_signature(string $rawBody, string $header, string $secret): bool {
    $provided = preg_replace('/^sha256=/', '', $header ?? '');
    $expected = hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $provided);
}

Retry behavior

Non-2xx responses are retried up to 5 times with exponential backoff starting at 1 minute. Keep your receiver idempotent using data.propertyId + event as the dedupe key.

Rate Limits

API requests are subject to your plan's monthly scoring and skip trace limits. Per-minute rate limiting will be added in a future update.

PlanScores/moSkip Traces/mo
Business5,000500
Founding Member10,0001,000

Typical Workflow

  1. Submit addresses via POST /api/v1/score
  2. Poll GET /api/v1/jobs/:id until status is complete — or skip polling by subscribing to the lead.scored webhook
  3. Retrieve scored leads via GET /api/v1/properties
  4. Enrich high-score properties via POST /api/v1/enrich/:id — receiving a lead.enriched webhook when owner data is attached

Need help? Contact support@uglyhouses.ai