> ## Documentation Index
> Fetch the complete documentation index at: https://qwady.wiki/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Quickstart

> Install and use the Borough TypeScript/JavaScript SDK

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @borough/sdk
  ```

  ```bash pnpm theme={null}
  pnpm add @borough/sdk
  ```

  ```bash yarn theme={null}
  yarn add @borough/sdk
  ```
</CodeGroup>

## Quick start

```typescript theme={null}
import { BoroughClient } from "@borough/sdk";

const borough = new BoroughClient("BOROUGH-...");

// Search rentals in Midtown
const results = await borough.rentals.search({
  areas: "120",
  minBeds: 1,
  maxPrice: 4000,
  noFee: true,
});

console.log(`Found ${results.meta.total} listings`);
for (const listing of results.data) {
  console.log(
    `${listing.address.street}${listing.address.unit ? ` ${listing.address.unit}` : ""} — $${listing.price}/mo`,
  );
}
```

## Configuration

The client accepts a string (API key) or a config object:

```typescript theme={null}
// Simple — just the API key
const borough = new BoroughClient("BOROUGH-...");

// Full config
const borough = new BoroughClient({
  apiKey: "BOROUGH-...",
  baseURL: "https://borough.qwady.app/v1", // default
  timeout: 30_000, // 30s default
  maxRetries: 2, // 2 retries on 429/5xx
});

// Environment variable fallback
// Set BOROUGH_API_KEY in your environment, then:
const borough = new BoroughClient();
```

## Resources

The client exposes resource modules following a consistent pattern:

```typescript theme={null}
// Search
const rentals = await borough.rentals.search({ areas: "120", minBeds: 2 });
const sales = await borough.sales.search({ areas: "200", maxPrice: 1_000_000 });

// Property details
const listing = await borough.listings.get("4961849");
const history = await borough.listings.history("4961849");
const fees = await borough.listings.fees("4961849");
const openHouses = await borough.listings.openHouses("4961849");

// Building data
const building = await borough.buildings.get("12345");
const scores = await borough.buildings.scores("12345");
const violations = await borough.buildings.violations("12345");
const buildingListings = await borough.buildings.listings("12345");

// Areas
const allAreas = await borough.areas.list();
const neighborhoods = await borough.areas.list({ level: 2, parentId: 100 });

// Market data
const snapshot = await borough.market.snapshot({ areaId: 120, bedrooms: 1 });
const trends = await borough.market.trends({ areaId: 120 });
const comparison = await borough.market.compare({ areas: "120,200,300" });
```

## Error handling

The SDK throws typed errors that you can catch and handle:

```typescript theme={null}
import {
  BoroughClient,
  NotFoundError,
  RateLimitError,
  QuotaExceededError,
} from "@borough/sdk";

const borough = new BoroughClient("BOROUGH-...");

try {
  const listing = await borough.listings.get("nonexistent");
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log("Listing not found");
  } else if (error instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}s`);
  } else if (error instanceof QuotaExceededError) {
    console.log("Monthly quota exceeded");
  } else {
    throw error;
  }
}
```

All error classes extend `APIError`, which extends `BoroughError`:

| Error Class           | Error Code                                              | When                                |
| --------------------- | ------------------------------------------------------- | ----------------------------------- |
| `AuthenticationError` | `MISSING_API_KEY`, `INVALID_API_KEY`, `EXPIRED_API_KEY` | Bad or missing API key              |
| `RateLimitError`      | `RATE_LIMIT_EXCEEDED`                                   | Too many requests per minute        |
| `QuotaExceededError`  | `QUOTA_EXCEEDED`                                        | Monthly request quota exceeded      |
| `NotFoundError`       | `NOT_FOUND`                                             | Resource doesn't exist              |
| `ValidationError`     | `INVALID_PARAMS`                                        | Bad request parameters              |
| `TierRestrictedError` | `TIER_RESTRICTED`                                       | Endpoint requires higher tier       |
| `UpstreamError`       | `UPSTREAM_ERROR`                                        | Data source temporarily unavailable |
| `ConnectionError`     | (none)                                                  | Network failure                     |

## Retries

The SDK automatically retries on `429` (rate limit) and `5xx` (server error) responses with exponential backoff. Default: 2 retries, starting at 500ms. The `Retry-After` header is respected when present.

```typescript theme={null}
// Disable retries
const borough = new BoroughClient({ apiKey: "...", maxRetries: 0 });
```

## TypeScript

The SDK is fully typed. All request parameters and response types are exported:

```typescript theme={null}
import type {
  ListingSummary,
  ListingDetail,
  BuildingDetail,
  BuildingScores,
  SearchParams,
  SaleSearchParams,
} from "@borough/sdk";
```

## Next steps

* [Auto-pagination](/borough/sdk/pagination): Iterate through all pages automatically
* [Webhook verification](/borough/sdk/webhooks): Verify webhook signatures in Express or Next.js
