@borough/sdk
v0.2.1
Published
Official TypeScript SDK for the Borough NYC Real Estate Data API
Maintainers
Readme
@borough/sdk
Official TypeScript SDK for the Borough NYC Real Estate Data API.
Installation
npm install @borough/sdk
# or
pnpm add @borough/sdk
# or
yarn add @borough/sdkQuick Start
import { BoroughClient } from "@borough/sdk";
const borough = new BoroughClient("BOROUGH_...");
// Search rentals in Manhattan under $3,500/mo
const results = await borough.rentals.search({
areas: "100",
maxPrice: 3500,
minBeds: 1,
});
console.log(`Found ${results.total} listings`);
for (const listing of results.data) {
console.log(`${listing.address.street} — $${listing.price}/mo`);
}
// Get full listing detail (50+ fields)
const { data: listing } = await borough.listings.get("4961849");
console.log(listing.amenities, listing.fees, listing.priceHistory);The client also reads BOROUGH_API_KEY from the environment:
const borough = new BoroughClient(); // uses process.env.BOROUGH_API_KEYResources
Rentals & Sales
const rentals = await borough.rentals.search({ areas: "301", noFee: true });
const sales = await borough.sales.search({ areas: "200", saleType: "CONDO" });Listings
const { data } = await borough.listings.get("4961849");
const { data: same } = await borough.listings.getByUrl(
"/building/123-main/apartment-4a"
);
const { data: history } = await borough.listings.history("4961849");
const { data: fees } = await borough.listings.fees("4961849");
const { data: openHouses } = await borough.listings.openHouses("4961849");Buildings
const { data: building } = await borough.buildings.get("12345");
const listings = await borough.buildings.listings("12345", {
status: "ACTIVE",
});
const { data: scores } = await borough.buildings.scores("12345");
const { data: violations } = await borough.buildings.violations("12345");Areas
const areas = await borough.areas.list({ level: 2, borough: 100 });Market
const { data: snapshot } = await borough.market.snapshot({ areaId: 301 });
const { data: trends } = await borough.market.trends({ areaId: 301 });
const { data: comparison } = await borough.market.compare({
areas: "301,302,303",
});Pagination
Search methods return a PaginatedResult that supports async iteration:
// Iterate through all pages automatically
for await (const listing of borough.rentals.search({ areas: "100" })) {
console.log(listing.address.street);
}
// Or collect into an array with an optional limit
const all = await results.toArray({ limit: 200 });
// Manual pagination
let page = await borough.rentals.search({ areas: "100" });
while (page) {
console.log(page.data.length, "listings on page", page.meta.page);
page = await page.nextPage();
}Webhook Verification
Verify inbound webhook signatures using the Express or Next.js adapter. Requires standardwebhooks as a peer dependency:
npm install standardwebhooksExpress
import express from "express";
import { webhookMiddleware } from "@borough/sdk/webhooks/express";
const app = express();
app.post(
"/webhooks/borough",
express.raw({ type: "application/json" }),
webhookMiddleware(process.env.WEBHOOK_SECRET!, (event) => {
console.log("Received event:", event.type, event.data);
})
);Next.js (App Router)
import { webhookHandler } from "@borough/sdk/webhooks/nextjs";
export const POST = webhookHandler(process.env.WEBHOOK_SECRET!, (event) => {
console.log("Received event:", event.type, event.data);
});Error Handling
All API errors are typed:
import {
RateLimitError,
QuotaExceededError,
NotFoundError,
} from "@borough/sdk";
try {
await borough.listings.get("invalid");
} catch (err) {
if (err instanceof NotFoundError) {
console.log("Listing not found");
} else if (err instanceof RateLimitError) {
console.log("Slow down — retry after a moment");
} else if (err instanceof QuotaExceededError) {
console.log("Monthly quota exhausted");
}
}Configuration
const borough = new BoroughClient({
apiKey: "BOROUGH_...",
baseURL: "https://borough.qwady.com/v1", // default
timeout: 30_000, // ms, default
maxRetries: 2, // exponential backoff with jitter
});Documentation
Full API reference and guides: docs.borough.qwady.app
Get an API key: Subscribe
License
MIT
