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

# Search Rentals

> Search active NYC rental listings with comprehensive filters. Returns
summary-level fields per listing. Use `/property/{id}` for full detail.

**Tier:** Free+




## OpenAPI

````yaml GET /search/rentals
openapi: 3.1.0
info:
  title: Borough API
  description: >
    NYC Real Estate Data API: structured access to rental and sale listings,

    building details, neighborhood data, and market statistics.


    ## Authentication


    All authenticated endpoints require a `Bearer` token in the `Authorization`
    header:

    ```

    Authorization: Bearer BOROUGH-<your-license-key>

    ```


    License keys are provisioned automatically when you subscribe via

    [Polar.sh](https://polar.sh/qwady-solutions-llc/portal). Free-tier endpoints

    (search, areas, market, and photos) work without authentication but are
    tracked per IP and subject to

    IP-based rate limits of 10 requests per minute. The health check endpoint
    remains fully public.


    ## Rate Limits


    | Tier     | Requests/min | Requests/month | Max perPage |

    |----------|-------------|----------------|-------------|

    | Free     | 10          | 100            | 10          |

    | Starter  | 30          | 5,000          | 50          |

    | Pro      | 60          | 25,000         | 500         |

    | Business | 120         | 100,000        | 500         |


    Tiered API responses include the `X-RateLimit-Limit` and `X-Quota-*`
    headers,

    plus an `X-Request-Id` header (UUID v4) for request tracing and debugging.

    Note that 429 responses additionally carry a `Retry-After` header and an

    `error.retryAfter` value in the body. The health check remains fully public

    and does not carry quota headers.


    ## Data Freshness


    Freshness-aware JSON data responses include a `meta.dataAge` field (ISO

    8601) indicating when the underlying data was last scraped, and a

    `meta.source` field indicating how the data was served (`cached`, `stale`,

    or `live`).


    Public cache cadence varies by dataset: rental search refreshes every 6h,

    sale search every 8h, listing detail sweeps every 12h, buildings daily at

    03:00 UTC, and market snapshots daily at 05:00 UTC. Broadband enrichment is

    checked monthly, but the underlying FCC source data is published only twice

    a year, so most checks are a no-op; broadband fields currently cover 76.5%

    of eligible buildings (those with an active listing and geocodes).


    Thresholds behave differently on detail routes than on search:


    - **Property and building detail**: a paid-tier request for data older than
      the tier's threshold queues a background refresh, so a subsequent request
      returns fresher data.
    - **Search**: crossing the threshold only changes the label. Results are
      returned as-is with `meta.source: "stale"` and **no refresh is queued**.
      Search freshness comes solely from the scrape cadence above, so a stale
      search generally means a scheduled scrape was missed. The 8h value is flat
      across every paid tier for that reason.
    - **Free tier**: never triggers a refresh on any route.


    | Tier     | Search Threshold (label only) | Listing Threshold | Building
    Threshold |

    |----------|-------------------------------|-------------------|--------------------|

    | Starter  | 8h                            | 30min             |
    6h                 |

    | Pro      | 8h                            | 15min             |
    3h                 |

    | Business | 8h                            | 10min             |
    2h                 |


    ## Photo URLs


    Listing and building responses include photo keys (32-character hex hashes).

    Use the Borough proxy for first-party image URLs:

    ```

    https://borough.qwady.app/v1/photos/{key}

    ```

    Supported sizes:

    ```

    large_800_400 (default), medium_500_250

    ```
  version: 1.0.0
  contact:
    name: Qwady Solutions LLC
    url: https://borough.qwady.app
    email: api@qwady.com
  license:
    name: Proprietary
    url: https://borough.qwady.app/terms
servers:
  - url: https://borough.qwady.app/v1
    description: Production
security:
  - BearerAuth: []
tags:
  - name: Search
    description: Search rental and sale listings with filters
  - name: Property
    description: Individual listing details and price history
  - name: Building
    description: Building information, amenities, and active listings
  - name: Areas
    description: Neighborhoods, boroughs, and geographic boundaries
  - name: Market
    description: Market snapshots, trends, and area comparisons
  - name: Webhooks
    description: Webhook subscriptions for listing change notifications
  - name: Watchers
    description: >-
      Persistent watchers that poll listings, buildings, and searches on a
      schedule
  - name: Streaming
    description: Server-Sent Events for live data and watcher changes
  - name: Utility
    description: Health checks and internal endpoints
paths:
  /search/rentals:
    get:
      tags:
        - Search
      summary: Search rental listings
      description: |
        Search active NYC rental listings with comprehensive filters. Returns
        summary-level fields per listing. Use `/property/{id}` for full detail.

        **Tier:** Free+
      operationId: searchRentals
      parameters:
        - $ref: '#/components/parameters/areas'
        - $ref: '#/components/parameters/excludeAreas'
        - $ref: '#/components/parameters/minPrice'
        - $ref: '#/components/parameters/maxPrice'
        - $ref: '#/components/parameters/minBeds'
        - $ref: '#/components/parameters/maxBeds'
        - $ref: '#/components/parameters/minBaths'
        - $ref: '#/components/parameters/maxBaths'
        - $ref: '#/components/parameters/buildingType'
        - $ref: '#/components/parameters/excludeBuildingType'
        - $ref: '#/components/parameters/noFee'
        - $ref: '#/components/parameters/minDaysOnMarket'
        - $ref: '#/components/parameters/maxDaysOnMarket'
        - $ref: '#/components/parameters/listedAfter'
        - $ref: '#/components/parameters/listedBefore'
        - $ref: '#/components/parameters/updatedSince'
        - $ref: '#/components/parameters/hasOpenHouse'
        - $ref: '#/components/parameters/has3dTour'
        - $ref: '#/components/parameters/minSqft'
        - $ref: '#/components/parameters/maxSqft'
        - $ref: '#/components/parameters/petsAllowed'
        - $ref: '#/components/parameters/amenities'
        - $ref: '#/components/parameters/maxMonthlyCost'
        - $ref: '#/components/parameters/minTransitScore'
        - $ref: '#/components/parameters/fiberOnly'
        - $ref: '#/components/parameters/minDownloadMbps'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/sortDir'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/perPage'
      responses:
        '200':
          description: Paginated list of rental listings
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-Quota-Limit:
              $ref: '#/components/headers/X-Quota-Limit'
            X-Quota-Used:
              $ref: '#/components/headers/X-Quota-Used'
            X-Quota-Remaining:
              $ref: '#/components/headers/X-Quota-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ListingSummary'
                  meta:
                    $ref: '#/components/schemas/PaginatedMeta'
              example:
                data:
                  - id: '4961849'
                    address:
                      street: 157 West 57th Street
                      unit: '#48B'
                    price: 27000
                    bedroomCount: 2
                    fullBathroomCount: 2
                    halfBathroomCount: 1
                    buildingType: CONDO
                    areaName: Midtown
                    status: ACTIVE
                    geoPoint:
                      latitude: 40.7654
                      longitude: -73.9791
                    leadPhotoKey: ed2987594843debc8e828fef155dbb6d
                    sourceGroupLabel: Douglas Elliman
                    urlPath: /building/one57-condominium/48b
                    listingType: rental
                meta:
                  total: 142
                  page: 1
                  perPage: 10
                  dataAge: '2026-02-12T14:30:00Z'
                  source: cached
        '400':
          $ref: '#/components/responses/BadRequest'
        '422':
          $ref: '#/components/responses/UnknownAreaIds'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
      security: []
components:
  parameters:
    areas:
      name: areas
      in: query
      description: >-
        Comma-separated area IDs (e.g., `120,115`). See `/areas` for the full
        list.
      schema:
        type: string
      example: 120,115
    excludeAreas:
      name: excludeAreas
      in: query
      description: >-
        Comma-separated area IDs to exclude from results. Unlike `areas`,
        borough-level IDs are matched exactly (no expansion to their child
        neighborhoods), and unknown IDs are ignored rather than returning a
        `422`.
      schema:
        type: string
      example: '115'
    minPrice:
      name: minPrice
      in: query
      schema:
        type: integer
      example: 2000
    maxPrice:
      name: maxPrice
      in: query
      schema:
        type: integer
      example: 5000
    minBeds:
      name: minBeds
      in: query
      schema:
        type: integer
      example: 1
    maxBeds:
      name: maxBeds
      in: query
      schema:
        type: integer
      example: 3
    minBaths:
      name: minBaths
      in: query
      schema:
        type: integer
    maxBaths:
      name: maxBaths
      in: query
      schema:
        type: integer
    buildingType:
      name: buildingType
      in: query
      description: >-
        Comma-separated building types. Valid values: RENTAL, CONDO, CO_OP,
        CONDOP, HOUSE, SINGLEFAMILY, TOWNHOUSE, TWOFAMILY, THREEFAMILY,
        FOURFAMILY, MULTI_FAMILY, MIXED_USE, HYBRID, COMMERCIAL, UNKNOWN.
        `MULTIFAMILY` (no underscore) is accepted as an input alias for
        `MULTI_FAMILY`; responses always return the canonical spelling.
      schema:
        type: string
      example: CONDO,CO_OP
    excludeBuildingType:
      name: excludeBuildingType
      in: query
      description: >-
        Comma-separated building types to exclude. Same valid values as
        `buildingType`: RENTAL, CONDO, CO_OP, CONDOP, HOUSE, SINGLEFAMILY,
        TOWNHOUSE, TWOFAMILY, THREEFAMILY, FOURFAMILY, MULTI_FAMILY, MIXED_USE,
        HYBRID, COMMERCIAL, UNKNOWN.
      schema:
        type: string
      example: HOUSE,TOWNHOUSE
    noFee:
      name: noFee
      in: query
      description: >-
        Filter for no-fee listings only. `noFee=true` matches only listings
        Borough has confirmed via a detail scrape are no-fee, and may undercount
        while listing-detail backfills complete; see `meta.warnings` in the
        response. `noFee=false` returns `400 INVALID_PARAMS`: Borough cannot yet
        distinguish "confirmed charges a fee" from "not yet detail-scraped," so
        a false-match would silently include every undetailed listing. Omit the
        parameter to return listings regardless of fee status.
      schema:
        type: boolean
    minDaysOnMarket:
      name: minDaysOnMarket
      in: query
      description: >-
        Minimum days on market. Computed from `listedAt`, which is a
        feed-first-seen estimate until a listing is detail-scraped; see
        `meta.warnings` in the response and `listedAtIsApproximate` on
        individual listings.
      schema:
        type: integer
        minimum: 0
    maxDaysOnMarket:
      name: maxDaysOnMarket
      in: query
      description: >-
        Maximum days on market. Same `listedAt` estimate caveat as
        `minDaysOnMarket`; see `meta.warnings` in the response.
      schema:
        type: integer
        minimum: 0
    listedAfter:
      name: listedAfter
      in: query
      description: >-
        Only listings listed on or after this date (`YYYY-MM-DD`). Datetimes are
        accepted but normalized to the UTC calendar day; any time component is
        ignored. `listedAt` is a feed-first-seen estimate until a listing is
        detail-scraped; see `meta.warnings` in the response.
      schema:
        type: string
        format: date
      example: '2026-01-01'
    listedBefore:
      name: listedBefore
      in: query
      description: >-
        Only listings listed on or before this date (`YYYY-MM-DD`). Datetimes
        are accepted but normalized to the UTC calendar day; any time component
        is ignored. Same `listedAt` estimate caveat as `listedAfter`; see
        `meta.warnings` in the response.
      schema:
        type: string
        format: date
    updatedSince:
      name: updatedSince
      in: query
      description: >-
        Delta sync: only listings last confirmed in the active feed at or after
        this date (ISO 8601). Returns rows whose `last_seen_at` is on or after
        the value, useful for incrementally pulling what changed since your last
        crawl. Includes newly listed rows. Matching is by calendar day; any time
        component is ignored.
      schema:
        type: string
        format: date
      example: '2026-06-01'
    hasOpenHouse:
      name: hasOpenHouse
      in: query
      description: >-
        Filter to listings with a scheduled open house. This is a detail-only
        signal and may undercount while listing-detail backfills complete; see
        `meta.warnings` in the response. `hasOpenHouse=false` returns `400
        INVALID_PARAMS` for the same reason as `noFee=false`: Borough cannot yet
        distinguish "confirmed no open house" from "not yet detail-scraped."
        Omit the parameter to return listings regardless of open-house status.
      schema:
        type: boolean
    has3dTour:
      name: has3dTour
      in: query
      description: >-
        Filter to listings with a 3D virtual tour. `has3dTour=true` matches only
        confirmed 3D-tour listings and may undercount while listing-detail
        backfills complete; see `meta.warnings` in the response.
        `has3dTour=false` returns `400 INVALID_PARAMS` for the same reason as
        `noFee=false`. Omit the parameter to return listings regardless of
        3D-tour status.
      schema:
        type: boolean
    minSqft:
      name: minSqft
      in: query
      description: >-
        Minimum square footage. `sqft` is a detail-only field: this drops every
        listing whose square footage hasn't been measured yet (not yet
        detail-scraped), not just listings outside the range; see
        `meta.warnings` in the response.
      schema:
        type: integer
        minimum: 1
      example: 500
    maxSqft:
      name: maxSqft
      in: query
      description: >-
        Maximum square footage. Same detail-only coverage caveat as `minSqft`;
        see `meta.warnings` in the response.
      schema:
        type: integer
        minimum: 1
      example: 1200
    petsAllowed:
      name: petsAllowed
      in: query
      description: Filter by listing-level pet flag or building-level pet policy
      schema:
        type: boolean
    amenities:
      name: amenities
      in: query
      description: >-
        Comma-separated amenity codes. Listings must match all requested
        amenities across listing- and building-level data. Listing-level
        amenities are detail-only and regex-derived from the listing
        description, so an amenity the description never mentions reads as
        absent, not merely unconfirmed; matches may undercount while
        listing-detail backfills complete. See `meta.warnings` in the response.
      schema:
        type: string
      example: DISHWASHER,WASHER_DRYER
    maxMonthlyCost:
      name: maxMonthlyCost
      in: query
      description: >-
        Maximum total monthly cost where maintenance, common charges, and
        property tax fields are populated. Falls back to price when those fields
        are unknown.
      schema:
        type: integer
        minimum: 0
    minTransitScore:
      name: minTransitScore
      in: query
      description: >-
        Minimum transit score (0-100) based on proximity and stop density within
        400 meters. Scores are strict; 80+ means exceptional transit clusters,
        not ordinary good subway access.
      schema:
        type: integer
        minimum: 0
        maximum: 100
    fiberOnly:
      name: fiberOnly
      in: query
      description: >-
        Require an FCC public-data provider match with fiber technology.
        Coverage may be exact-building or Census-block level depending on
        imported area.
      schema:
        type: boolean
    minDownloadMbps:
      name: minDownloadMbps
      in: query
      description: >-
        Require an FCC public-data provider match with at least this advertised
        download speed in Mbps. Coverage may be exact-building or Census-block
        level depending on imported area.
      schema:
        type: integer
        minimum: 0
    sort:
      name: sort
      in: query
      description: >-
        Sort attribute. `sort=LISTED_AT` orders by `listedAt`, which is a
        feed-first-seen estimate until a listing is detail-scraped; this can
        interleave estimated and authoritative dates, ranking an undetailed
        listing as newer than it really is. See `meta.warnings` in the response.
      schema:
        type: string
        enum:
          - RECOMMENDED
          - PRICE
          - LISTED_AT
        default: RECOMMENDED
    sortDir:
      name: sortDir
      in: query
      description: Sort direction
      schema:
        type: string
        enum:
          - ASC
          - DESC
        default: DESC
    page:
      name: page
      in: query
      description: Page number (1-indexed)
      schema:
        type: integer
        minimum: 1
        default: 1
    perPage:
      name: perPage
      in: query
      description: >-
        Results per page. Maximum depends on tier: Free=10, Starter=50,
        Pro/Business=500.
      schema:
        type: integer
        minimum: 1
        maximum: 500
        default: 10
  headers:
    X-Request-Id:
      description: >-
        Unique trace ID for this request (UUID v4). Include in support requests
        for log correlation.
      schema:
        type: string
        format: uuid
    X-RateLimit-Limit:
      description: Maximum requests per minute
      schema:
        type: integer
    X-Quota-Limit:
      description: Total requests allowed this billing period
      schema:
        type: integer
    X-Quota-Used:
      description: Requests consumed this billing period
      schema:
        type: integer
    X-Quota-Remaining:
      description: Remaining requests this billing period
      schema:
        type: integer
    X-Quota-Tier:
      description: The caller's current tier. Sent on QUOTA_EXCEEDED (429) responses.
      schema:
        type: string
    X-Quota-Upgrade-URL:
      description: URL to upgrade to a higher tier. Sent on QUOTA_EXCEEDED (429) responses.
      schema:
        type: string
        format: uri
  schemas:
    ListingSummary:
      type: object
      properties:
        id:
          type: string
        address:
          $ref: '#/components/schemas/Address'
        price:
          type: integer
        netEffective:
          type:
            - integer
            - 'null'
        monthsFree:
          type:
            - integer
            - 'null'
        leaseTermMonths:
          type:
            - integer
            - 'null'
        bedroomCount:
          type:
            - integer
            - 'null'
        fullBathroomCount:
          type:
            - integer
            - 'null'
        halfBathroomCount:
          type:
            - integer
            - 'null'
        sqft:
          description: >-
            Interior square footage. Populated only on detail-scraped listings;
            null otherwise.
          type:
            - integer
            - 'null'
        pricePerSqft:
          description: >-
            Price divided by sqft, rounded to the nearest dollar. Null when sqft
            is unavailable.
          type:
            - integer
            - 'null'
        buildingType:
          description: >-
            Canonical building type. Responses always use these spellings —
            `MULTIFAMILY` is only an input alias for `MULTI_FAMILY` on the
            search filters.
          oneOf:
            - type: string
              enum:
                - CONDO
                - CO_OP
                - CONDOP
                - RENTAL
                - TOWNHOUSE
                - HOUSE
                - SINGLEFAMILY
                - TWOFAMILY
                - THREEFAMILY
                - FOURFAMILY
                - MULTI_FAMILY
                - MIXED_USE
                - HYBRID
                - COMMERCIAL
                - UNKNOWN
            - type: 'null'
        areaName:
          type:
            - string
            - 'null'
        areaId:
          type:
            - integer
            - 'null'
        status:
          type: string
          enum:
            - ACTIVE
            - IN_CONTRACT
            - OFF_MARKET
          description: >-
            Borough currently emits ACTIVE and OFF_MARKET only. IN_CONTRACT is a
            reserved, forward-compatible value not yet surfaced by the data
            feed.
        geoPoint:
          oneOf:
            - $ref: '#/components/schemas/GeoPoint'
            - type: 'null'
        leadPhotoKey:
          type:
            - string
            - 'null'
          description: '32-char hex key. Preferred first-party URL: `/v1/photos/{key}`.'
        sourceGroupLabel:
          type:
            - string
            - 'null'
          description: Brokerage name
        urlPath:
          type: string
        listingType:
          type: string
          enum:
            - rental
            - sale
        listedAt:
          type:
            - string
            - 'null'
          format: date
          description: >-
            Date the listing went on market (YYYY-MM-DD). For listings not yet
            detail-scraped this is the date Borough first observed the listing,
            accurate to within one feed cycle. See listedAtIsApproximate.
        listedAtIsApproximate:
          type: boolean
          description: >-
            True when listedAt is the feed-first-seen estimate (the listing has
            not yet been detail-scraped), false when it is the authoritative
            on-market date from the listing detail page. daysOnMarket inherits
            this accuracy, so use the flag before relying on listedAt /
            daysOnMarket for date filtering or time-on-market analysis.
        availableAt:
          type:
            - string
            - 'null'
          format: date
        daysOnMarket:
          type:
            - integer
            - 'null'
          description: >-
            Derived from listedAt; null when listedAt is unknown. Carries the
            same accuracy as listedAt (see listedAtIsApproximate).
        buildingId:
          type:
            - string
            - 'null'
          description: ID of the parent building, or null.
        buildingScores:
          type:
            - object
            - 'null'
          description: >-
            Building-level scores for the parent building, or null when the
            building has no scores.
          properties:
            healthScore:
              type:
                - number
                - 'null'
            noiseScore:
              type:
                - number
                - 'null'
            safetyScore:
              type:
                - number
                - 'null'
            transitScore:
              type:
                - number
                - 'null'
            violationsOpen:
              type:
                - number
                - 'null'
            updatedAt:
              type:
                - string
                - 'null'
        noFee:
          type:
            - boolean
            - 'null'
          description: >-
            Whether the listing is broker-fee-free. `null` means this listing
            has not yet been detail-scraped, so Borough has no observed value
            for it; it is not a signal that the listing charges a fee. See
            `detailScraped` below, and the [data freshness
            guide](/borough/guides/data-freshness) for the scope of this gap
            (currently around 55% of active listings; the exact share varies
            over time).
        detailScraped:
          type: boolean
          description: >-
            `true` once a detail scrape has run for this listing. This is weaker
            than it looks: a detail scrape that ran but failed to parse a given
            field (e.g. an upstream key rename) still sets this `true` while
            that field stays `null`. Read it as "Borough attempted a detail
            scrape," not "every detail-only field on this listing was observed."
        priceDelta:
          type:
            - integer
            - 'null'
    PaginatedMeta:
      allOf:
        - $ref: '#/components/schemas/Meta'
        - type: object
          properties:
            total:
              type: integer
            page:
              type: integer
            perPage:
              type: integer
            links:
              $ref: '#/components/schemas/PaginationLinks'
            warnings:
              type: array
              items:
                type: string
              description: >-
                Data coverage or behavior caveats for requested filters. Present
                when one or more of the following are used, since each depends
                on data that is detail-only or feed-estimated and so may
                under-return against the full population:
                `fiberOnly`/`minDownloadMbps`, `minTransitScore`,
                `has3dTour=true`, `noFee=true`, `hasOpenHouse=true`,
                `minSqft`/`maxSqft`, `amenities`, and the `listedAt`-derived
                filters (`minDaysOnMarket`, `maxDaysOnMarket`, `listedAfter`,
                `listedBefore`, `sort=LISTED_AT`). See the [data freshness
                guide](/borough/guides/data-freshness) and [filtering
                guide](/borough/guides/filtering).
    Address:
      type: object
      properties:
        street:
          type: string
          example: 157 West 57th Street
        unit:
          type:
            - string
            - 'null'
          example: '#48B'
    GeoPoint:
      type: object
      properties:
        latitude:
          type: number
          format: double
          example: 40.7654
        longitude:
          type: number
          format: double
          example: -73.9791
    Meta:
      type: object
      properties:
        dataAge:
          type:
            - string
            - 'null'
          format: date-time
          description: When the underlying data was last refreshed (ISO 8601)
        source:
          type: string
          enum:
            - cached
            - stale
            - live
          description: >
            How this data was served:

            - `cached`: From D1 database, within the tier's freshness threshold.

            - `stale`: From D1 but older than the tier's freshness threshold. On
              property and building detail this also queues a background refresh
              for paid tiers. On **search** it is a label only; the results are
              returned as-is and no refresh is queued, on any tier.
            - `live`: Fetched live from source (Business/Internal tiers on
            property
              and building detail endpoints).
        freshnessThreshold:
          type:
            - integer
            - 'null'
          description: >-
            Freshness threshold in minutes for the current tier. Emitted only on
            property and building detail, where paid-tier data older than this
            queues a background refresh. Search applies a threshold internally
            to decide whether `source` reads `cached` or `stale`, but does not
            emit this field; do not expect it on a search response. Also omitted
            on routes that do not evaluate a threshold at all.
        refreshTriggered:
          type:
            - boolean
            - 'null'
          description: >-
            Whether a background refresh was queued for this request. Only
            property and building detail routes queue refreshes; search never
            does, so this is never `true` on a search response.
        detailScraped:
          type: boolean
          description: >-
            Present on `/property/{id}/history`, `/property/{id}/fees`, and
            `/property/{id}/open-houses`. Disambiguates an empty `data` array:
            `false` means this listing has never been detail-scraped, so the
            list was never checked, not "checked, and there's nothing there."
            When `false`, `dataAge` is also `null` for the same reason. See the
            [data freshness guide](/borough/guides/data-freshness).
    PaginationLinks:
      type: object
      description: Pagination navigation links
      properties:
        self:
          type: string
          format: uri
        first:
          type: string
          format: uri
        last:
          type: string
          format: uri
        prev:
          type:
            - string
            - 'null'
          format: uri
        next:
          type:
            - string
            - 'null'
          format: uri
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          required:
            - code
            - message
            - status
          properties:
            code:
              type: string
              enum:
                - INVALID_PARAMS
                - MISSING_API_KEY
                - INVALID_API_KEY
                - EXPIRED_API_KEY
                - TIER_RESTRICTED
                - QUOTA_EXCEEDED
                - NOT_FOUND
                - WEBHOOK_NOT_FOUND
                - RATE_LIMIT_EXCEEDED
                - INTERNAL_ERROR
                - UPSTREAM_ERROR
                - SERVICE_DISABLED
            message:
              type: string
            status:
              type: integer
            retryAfter:
              type:
                - integer
                - 'null'
              description: Seconds to wait before retrying (for 429 responses)
            quotaContext:
              type:
                - object
                - 'null'
              description: >-
                Upgrade context included on QUOTA_EXCEEDED (429) responses; null
                or absent otherwise.
              properties:
                currentTier:
                  type: string
                currentQuota:
                  type: integer
                nextTier:
                  type:
                    - string
                    - 'null'
                nextTierQuota:
                  type:
                    - integer
                    - 'null'
                upgradeUrl:
                  type: string
                  format: uri
  responses:
    BadRequest:
      description: Invalid parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: INVALID_PARAMS
              message: Parameter 'maxPrice' must be a positive integer.
              status: 400
    UnknownAreaIds:
      description: >-
        One or more `areas` values are numeric but are not known area IDs. The
        message lists the unrecognized IDs comma-separated, and is singular
        (`Unknown area ID: 999999`) when exactly one is unrecognized.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: INVALID_PARAMS
              message: 'Unknown area IDs: 999999,888888'
              status: 422
    RateLimited:
      description: Rate limit or quota exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until rate limit resets
        X-Quota-Tier:
          $ref: '#/components/headers/X-Quota-Tier'
        X-Quota-Upgrade-URL:
          $ref: '#/components/headers/X-Quota-Upgrade-URL'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            rateLimit:
              summary: Per-minute rate limit exceeded
              value:
                error:
                  code: RATE_LIMIT_EXCEEDED
                  message: Rate limit exceeded. Retry after 32 seconds.
                  status: 429
                  retryAfter: 32
            quotaExceeded:
              summary: Monthly quota exhausted
              value:
                error:
                  code: QUOTA_EXCEEDED
                  message: >-
                    Monthly request quota exhausted. Upgrade your plan to
                    continue.
                  status: 429
                  quotaContext:
                    currentTier: starter
                    currentQuota: 5000
                    nextTier: pro
                    nextTierQuota: 25000
                    upgradeUrl: https://polar.sh/qwady-solutions-llc/portal
    InternalError:
      description: Unexpected server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: INTERNAL_ERROR
              message: >-
                An unexpected error occurred. Please retry or contact support
                with the X-Request-Id.
              status: 500
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: BOROUGH-<uuid>
      description: 'Polar.sh license key. Format: `BOROUGH-<uuid>`'

````