Public API
SellerSignal scoring

Build on explainable seller intelligence.

Search a territory, inspect the signals behind a property score, or export a clean CSV for your workflow. Every scoring request is protected by a brokerage-scoped API key.

01 · Quickstart

One authenticated request to find a territory signal.

Set your deployment origin and brokerage API key, then call the search route. Every successful item includes a score, human-readable explanations, and the full factor breakdown.

GET · territory search

export SELLERSIGNAL_URL="https://your-deployment.example"
export SELLERSIGNAL_API_KEY="ss_live_your-key"

curl --get "$SELLERSIGNAL_URL/api/territory-search" \
  --header "Authorization: Bearer $SELLERSIGNAL_API_KEY" \
  --data-urlencode "zip=94110" \
  --data-urlencode "equityMin=40" \
  --data-urlencode "listingStatus=expired" \
  --data-urlencode "listingStatus=withdrawn" \
  --data-urlencode "outcome=expired,withdrawn"

Replace the example origin with your deployment URL. The `zip` parameter is required; all other filters are optional.

02 · Authentication

Brokerage-scoped access for every scoring route.

Create a SellerSignal API key from an authenticated brokerage owner session, then send it as an HTTP Bearer token on territory search, property detail, and CSV export. Keys are scoped to the brokerage that created them.

Send the key with every scoring request

Include Authorization: Bearer <ss_live_token> on every scoring request. The server validates the hash, rejects revoked or unknown keys, and returns HTTP 401 with { error: "Unauthorized" } when credentials are missing or invalid.

The owner-scoped lifecycle is /api/brokerages/{id}/api-key; it requires a signed-in brokerage owner session. The raw token is returned only once from the create response.

Manage brokerage API keys
POST
Create one ss_live_ token; returned once in a 201 response.
GET
Read key metadata only; the raw token is never returned again.
DELETE
Revoke the active key; the operation is idempotent.

03 · API errors

Know the failure envelopes before you ship.

Scoring routes use a small set of stable error responses. Match the HTTP status first, then use the response body and troubleshooting note to correct the request without changing the live API contract.

Every scoring response includes an x-request-id header. JSON error responses also include the same UUID in requestId; use it when sharing a failure with support. Successful JSON and CSV response bodies stay unchanged.

401 · Unauthorized
Missing or invalid API key

Endpoint/context: every SellerSignal scoring route when the Bearer credential is missing, malformed, unknown, or revoked.

Response

{ "error": "Unauthorized", "requestId": "550e8400-e29b-41d4-a716-446655440000" }

Troubleshooting: Send the key as Authorization: Bearer <ss_live_token>. Check that the token is complete, uses the Bearer scheme, and has not been revoked.

400 · Bad request
Malformed search or export query

Endpoint/context: GET /api/territory-search or GET /api/territory-search/export with a valid API key but invalid query values.

Example request · GET /api/territory-search?equityMin=101

{ "errors": { "zip": "Enter a 5-digit ZIP", "equityMin": "Number must be less than or equal to 100" }, "requestId": "550e8400-e29b-41d4-a716-446655440000" }

Troubleshooting: Include a five-digit zip and keep equityMin and equityMax within the inclusive 0–100 range. The same field-error envelope is used by JSON search and CSV export.

403 · Forbidden
Unauthorized brokerage scope

Endpoint/context: a server-side brokerage-scope check where a valid key belongs to a different requested brokerage.

Response

{ "error": "Forbidden", "requestId": "550e8400-e29b-41d4-a716-446655440000" }

Troubleshooting: Use the key issued for the brokerage the server-side integration is checking. Public scoring routes resolve brokerage scope from the key and do not expose a public brokerage selector or query parameter.

500 · Internal server error
Unexpected scoring failure

Endpoint/context: any scoring route when authentication, scoring, or a data lookup fails unexpectedly.

Response

{ "error": "Internal Server Error", "requestId": "550e8400-e29b-41d4-a716-446655440000" }

Troubleshooting: Include the x-request-id value when reporting the failure so it can be correlated with server logs.

05 · Property detail

Inspect one score without losing its context.

GET /api/properties/{id} returns the same PropertyItem shape as search, including the computed photoUrl field. The id must be a Prisma cuid.

GET · property detail

curl --header "Authorization: Bearer $SELLERSIGNAL_API_KEY" \
  "$SELLERSIGNAL_URL/api/properties/$PROPERTY_ID"

Responses

200
{ property: PropertyItem }
401
Missing, revoked, or invalid brokerage API key.
400
Malformed cuid.
404
No property for that id.

06 · Export

Take the same search pipeline with you.

`GET /api/territory-search/export` accepts the same query contract as JSON search and returns a UTF-8 CSV attachment. It is useful for a CRM import or a quick offline review.

GET · CSV export

curl --fail --location --output territory-search.csv \
  --get "$SELLERSIGNAL_URL/api/territory-search/export" \
  --header "Authorization: Bearer $SELLERSIGNAL_API_KEY" \
  --data-urlencode "zip=94110" \
  --data-urlencode "listingStatus=expired"
Exact CSV columns
address
zip
owner_name
equity_pct
equity_dollars
listing_status
listing_outcome
days_on_market
score
score_breakdown
photo_url

Invalid queries return the same HTTP 400 { errors: { field: "message" } } JSON envelope as the search route.

07 · Response

A property record is transparent by design.

Search and detail share one `PropertyItem` contract. Nullable fields are explicit; `scoreFactors` carries the raw value, weight, direction, and explanation for each row.

FieldTypeDescription
idstringPrisma cuid used by the property detail endpoint.
zipstringFive-digit ZIP code.
addressstringProperty street address.
ownerNamestringOwner name from the property record.
estimatedValueintegerEstimated property value in dollars.
mortgageBalanceintegerEstimated mortgage balance in dollars.
listingStatusenumlisted | off_market | expired | withdrawn | coming_soon.
listingOutcomeenumactive | expired | withdrawn | canceled | none.
daysOnMarketintegerCurrent or most recent days on market.
ownerOccupiedbooleanWhether the owner occupies the property.
absenteeYearsintegerYears the record has indicated absentee ownership.
lastSaleDateISO date | nullMost recent recorded sale date.
lastSalePriceinteger | nullMost recent recorded sale price in dollars.
yearBuiltintegerYear the property was built.
sqftintegerRecorded square footage.
beds / bathsintegerRecorded bedroom and bathroom counts.
neighborhoodTrendenumrising | stable | cooling.
medianDomintegerNeighborhood median days on market.
neighborhoodScoreintegerCurrent neighborhood score.
equityPct / equityDollarsintegerComputed equity percentage and dollars.
scoreintegerClamped Seller Signal Score from 0 through 100.
explanationstring[]Non-zero score explanations, with a fallback when none apply.
scoreFactorsobject[]Detailed factor rows used to reconcile the headline score.
outreachCopystringGenerated outreach draft based on the property signals.
photoUrlstring | nullProperty photo URL when available.
leadSignalsobject | omittedCRM flags when a linked lead exists.

08 · Scoring

Every score has a legible reason behind it.

Callers control the candidate set with filters. The server then computes a deterministic 0–100 score, returns non-zero `explanation` strings, and includes the full `scoreFactors[]` array. Factor weights reconcile to the displayed score after clamping.

FactorWeightApplied when
Long-tenure ownership+1510+ years owned or no prior sale on record
Absentee ownership+15 / −5>5 absentee years and not occupied / owner-occupied
Equity position+20 / +10>40% equity / 15–40% equity
Current listing status+20 / +15 / +5expired or withdrawn / coming soon / listed
Prior listing outcome+5expired, withdrawn, or canceled
Days on market+15 / +10>180 days / >90 days while currently listed
Neighborhood trend+5 / −5cooling / rising
Agent relationship+15 / +8past client / referral, when supplied by the app
CRM flags−100 / −25 / +10do-not-contact / unanswered outreach / owner note
Factor object
{ key, name, rawValue, weight, direction, explanation }

`direction` is `up`, `down`, or `neutral`. Zero-weight factors stay in the breakdown so the caller can see the evaluated signal.

Clamping

Scores are clamped to 0–100. When the raw total crosses either boundary, the server adds a `clamp_high` or `clamp_low` factor so the weights still reconcile to the headline score.

Ready to explore

Create a brokerage workspace to manage the API-key lifecycle and keep your team’s integration settings in one place.

Open brokerages