REST API

A versioned HTTP API at https://api.nosyneighbor.nyc/v1/*. Every response is JSON. Every request other than GET /v1/usage is a POST with a JSON body.

Base URL. The API is https://api.nosyneighbor.nyc. The web app at https://nosyneighbor.nyc does not serve /v1/*. A request sent there returns a 404 that repeats this sentence.

1. Get an API key

Sign in, open the developer dashboard, and create a key. The full key is shown exactly once. Copy it immediately, because it cannot be retrieved again (you can only revoke or rotate it after that). Production keys start with nn_live_. Staging keys start with nn_test_. A key created against one environment is never valid against the other, whatever prefix it is given.

2. Authenticate

Send the key as a bearer token on every request:

Authorization: Bearer nn_test_0000000000000000_<paste-your-own-secret-here>

That key is a placeholder. See the security note at the bottom of this page. No other header, cookie, or query parameter authenticates this API.

3. Quickstart with curl

curl -s https://api.nosyneighbor.nyc/v1/lookup \
  -H "Authorization: Bearer nn_test_0000000000000000_<paste-your-own-secret-here>" \
  -H "Content-Type: application/json" \
  -d '{"address": "20 W 34th St, New York NY 10001"}'

The operations

PathPurpose
POST /v1/lookupFull property report for one NYC address
POST /v1/searchBrowse listings in a ZIP code or area with filters
POST /v1/compsComparable sold homes, or a valuation, for an address
POST /v1/rentalsComparable active rentals, or a rent estimate, for an address
POST /v1/updatesDigest of new, price-dropped, and off-market listings for an area
POST /v1/ratesCurrent US mortgage rates for a loan scenario
GET /v1/usageYour current rate-limit and quota usage (free, costs nothing)

Every field, request shape, and response shape is in the interactive API reference (generated from the same registry that defines the operations, so it cannot drift from what the API serves). This page is a guide, not the full schema.

Response envelope

A successful call always returns:

{
  "data": { /* operation-specific result */ },
  "meta": {
    "request_id": "…",
    "operation": "lookupProperty",
    "units_charged": 10,
    "minute_remaining": 230,
    "daily_remaining": 9990
  }
}

Error envelope

Every failure, whatever the cause, returns the same JSON shape, with a matching HTTP status:

{
  "code": "rate_limited",
  "message": "You've hit the per-minute rate limit for the Pro REST API.",
  "request_id": "…"
}

code is a small closed set: unauthorized (401), forbidden (403), invalid_request (400), not_found (404), outside_coverage (400), rate_limited (429), quota_unavailable (503), method_not_allowed (405), upstream_error (502), and internal_error (500). Keep request_id if you need to report a problem. It is also echoed as the x-request-id response header on every request except CORS preflights. The next section tells outside_coverage, not_found, and invalid_request apart.

Coverage vs. not found

These response codes are easy to mix up:

CodeStatusMeaning
outside_coverage400The address or area sits outside the five boroughs. Coverage is NYC only, and a different spelling will not change the answer.
not_found404The address is in NYC, but it has no tax lot on record, or the API cannot place it on a map.
invalid_request400The request is malformed, or it carries a field the operation does not accept. An unknown field names each one: Unknown field(s): city, state, zip. POST /v1/lookup accepts: address, include_comps, include_rentals.

Anatomy of a lookup

POST /v1/lookup returns these fields inside data.

See the interactive API reference for every per-source field.

Units and formats

Money fields report US dollars. Most are whole numbers. A few carry a decimal fraction for cents, such as tax_amount. No field states an amount in cents. balance_due can be negative, which the city's ledger records when payments exceed the penalty imposed.

Every date value comes straight from the NYC agency that publishes it, in whatever format that agency uses. Most sources use ISO YYYY-MM-DD. nyc_ecb uses YYYYMMDD for issue_date and hearing_date. nyc_dob uses it for issue_date and disposition_date. nyc_dob_jobs uses MM/DD/YYYY for pre__filing_date. The API never reformats these strings.

Searching

Set location (a ZIP or a borough neighborhood name) or zip_codes (an array of 5-digit NYC ZIPs) to pick an area. Page results with limit and offset. has_more is true when another page exists at offset + results.length. There is no total count.

source_status reports one entry per listing call. Its keys are cached when the answer comes from the daily-refreshed index (the normal case), the listing source's own name, such as redfin, on the rare live call, and not_warm when the area has no entitlement or no coverage yet. Redfin is currently the only listing source, so each result's own sources map has one key and its discrepancies list is always empty. Both fields stay in the shape so a second source can join later without a shape change.

Retries and 429 handling

A 429 rate_limited response includes a Retry-After header, in seconds. Wait at least that long before you retry the same call.

A permanent refusal is the exception. It happens when the operation's cost exceeds your entire per-minute or per-day ceiling. No Retry-After is offered, and a retry can never succeed. Raise the limit instead.

The RateLimit and RateLimit-Policy headers follow the IETF draft shape. They appear on responses sent after your account's limits are known, which means successes and 429s. They report whichever budget is tighter, per-minute or per-day. The JSON meta block is the stable contract to read against, and the headers are a convenience.

A 502 or a 503 is safe to retry with backoff. A 4xx other than 429 does not succeed on retry unless you change the request. See Rate Limits for the full numbers.

Versioning policy

The current (and only) version is v1. Once published, a response field is not removed or repurposed and an error code keeps its HTTP status and meaning: a new failure mode is mapped onto the closest existing code rather than growing the set. A breaking change arrives as a new version prefix (/v2/*), never a silent change under /v1/*.

Security note: every key shown on this page (nn_test_0000000000000000_<paste-your-own-secret-here>) is a placeholder. The bracketed part is never a real secret. Get a real key from the developer dashboard, and never commit one to source control.
← Back to Nosy Neighbor Developer dashboard Data Glossary Terms of Use