openapi: 3.0.3
info:
  title: Borough NYC Real Estate API (Free Tier)
  description: >
    Search NYC rental and sale listings, browse neighborhoods, and get

    autocomplete suggestions — powered by structured NYC real estate data.


    ## Free Tier Limits


    | Limit            | Value       |

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

    | Requests/minute  | 10          |

    | Requests/month   | 100         |

    | Max perPage      | 10          |


    ## Upgrade for More


    Need full listing details, building data, market analytics, or higher

    limits? Subscribe to a paid plan via

    [Polar.sh](https://polar.sh/qwady-solutions-llc/portal) and use the

    full Borough API directly at `borough.qwady.app/v1`.


    | Tier     | Price   | Requests/mo |
    Features                                    |

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

    | Starter  | $19/mo  | 5,000       | Full listing details, building
    data          |

    | Pro      | $49/mo  | 25,000      | Market analytics, fresher
    data               |

    | Business | $99/mo  | 100,000     | Webhooks, neighborhood stats, fastest
    refresh|


    [Get a paid API key →](https://polar.sh/qwady-solutions-llc/portal)
  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:
  - RapidAPI: []
tags:
  - name: Search
    description: Search rental and sale listings with filters
  - name: Areas
    description: Neighborhoods, boroughs, and geographic boundaries
  - name: Utility
    description: Autocomplete, photo URLs, and health
paths:
  /search/rentals:
    get:
      operationId: searchRentals
      summary: Search rental listings
      description: |
        Search active NYC rental listings with comprehensive filters. Returns
        summary-level fields per listing. Max 10 results per page on free tier.

        **Upgrade** to get full listing details via `/property/{id}` — available
        on [paid plans](https://polar.sh/qwady-solutions-llc/portal).
      tags:
        - Search
      parameters:
        - $ref: '#/components/parameters/areas'
        - $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/noFee'
        - $ref: '#/components/parameters/minSqft'
        - $ref: '#/components/parameters/maxSqft'
        - $ref: '#/components/parameters/petsAllowed'
        - $ref: '#/components/parameters/amenities'
        - $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
          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'
                      city: NEW YORK
                      state: NY
                      zipCode: '10019'
                    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'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /search/sales:
    get:
      operationId: searchSales
      summary: Search sale listings
      description: >
        Search active NYC sale listings. Supports all rental filters plus

        `saleType` (resale, sponsor, foreclosure). Max 10 results per page on
        free tier.


        **Upgrade** to get full listing details, building data, and market
        analytics

        on [paid plans](https://polar.sh/qwady-solutions-llc/portal).
      tags:
        - Search
      parameters:
        - $ref: '#/components/parameters/areas'
        - $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'
        - name: saleType
          in: query
          description: Sale type filter
          schema:
            type: string
            enum:
              - RESALE
              - SPONSOR_UNIT
              - FORECLOSURE
        - $ref: '#/components/parameters/noFee'
        - $ref: '#/components/parameters/minSqft'
        - $ref: '#/components/parameters/maxSqft'
        - $ref: '#/components/parameters/petsAllowed'
        - $ref: '#/components/parameters/amenities'
        - $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 sale listings
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ListingSummary'
                  meta:
                    $ref: '#/components/schemas/PaginatedMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /areas:
    get:
      operationId: listAreas
      summary: List areas and neighborhoods
      description: |
        Returns all 349 NYC areas including boroughs, sub-boroughs, and
        neighborhoods. Optionally filter by level or parent area.
      tags:
        - Areas
      parameters:
        - name: level
          in: query
          description: 'Filter by hierarchy level: 0=borough, 1=sub-borough, 2=neighborhood'
          schema:
            type: integer
            enum:
              - 0
              - 1
              - 2
        - name: parentId
          in: query
          description: >-
            Filter by parent area ID (100=Manhattan, 200=Bronx, 300=Brooklyn,
            400=Queens, 500=Staten Island)
          schema:
            type: integer
        - name: q
          in: query
          description: >-
            Case-insensitive area name or slug lookup. Exact name matches are
            returned first, followed by broader suggestions.
          schema:
            type: string
            minLength: 1
        - name: include
          in: query
          description: Include optional boundary data in each area record.
          schema:
            type: string
            enum:
              - boundary
      responses:
        '200':
          description: List of areas
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Area'
                  meta:
                    $ref: '#/components/schemas/PaginatedMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'
  /health:
    get:
      operationId: healthCheck
      summary: Health check
      description: Public uptime check. Returns service status and timestamp.
      tags:
        - Utility
      responses:
        '200':
          description: Service healthy
          content:
            application/json:
              schema:
                type: object
                required:
                  - status
                  - timestamp
                properties:
                  status:
                    type: string
                    example: ok
                  timestamp:
                    type: string
                    format: date-time
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'
      security: []
components:
  securitySchemes:
    RapidAPI:
      type: apiKey
      in: header
      name: X-RapidAPI-Key
      description: Provided automatically by RapidAPI when you subscribe.
  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
    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
    minSqft:
      name: minSqft
      in: query
      description: Minimum square footage
      schema:
        type: integer
        minimum: 1
      example: 500
    maxSqft:
      name: maxSqft
      in: query
      description: Maximum square footage
      schema:
        type: integer
        minimum: 1
      example: 1200
    buildingType:
      name: buildingType
      in: query
      description: Comma-separated building types
      schema:
        type: string
      example: CONDO,CO_OP
    noFee:
      name: noFee
      in: query
      schema:
        type: boolean
    petsAllowed:
      name: petsAllowed
      in: query
      description: Filter to listings that explicitly allow pets
      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.
      schema:
        type: string
      example: DISHWASHER,WASHER_DRYER
    fiberOnly:
      name: fiberOnly
      in: query
      description: >-
        Require a building-level FCC broadband provider record with fiber
        technology. Provisional while Borough validates the underlying FCC and
        CostQuest contract path for further rollout.
      schema:
        type: boolean
    minDownloadMbps:
      name: minDownloadMbps
      in: query
      description: >-
        Require a building-level FCC broadband provider record with at least
        this advertised download speed in Mbps. Provisional while Borough
        validates the underlying FCC and CostQuest contract path for further
        rollout.
      schema:
        type: integer
        minimum: 0
    sort:
      name: sort
      in: query
      description: Sort attribute
      schema:
        type: string
        enum:
          - RECOMMENDED
          - PRICE
          - LISTED_AT
        default: PRICE
    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 (max 10 on free tier)
      schema:
        type: integer
        minimum: 1
        maximum: 10
        default: 10
  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
    RateLimited:
      description: Rate limit exceeded (10 requests/minute)
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until rate limit resets
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Rate limit exceeded. Retry after 32 seconds.
              status: 429
              retryAfter: 32
    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
  schemas:
    Meta:
      type: object
      properties:
        dataAge:
          type: string
          format: date-time
          description: When the data was last refreshed (ISO 8601)
        source:
          type: string
          enum:
            - cached
            - stale
    PaginatedMeta:
      allOf:
        - $ref: '#/components/schemas/Meta'
        - type: object
          properties:
            total:
              type: integer
            page:
              type: integer
            perPage:
              type: integer
    Address:
      type: object
      properties:
        street:
          type: string
          example: 157 West 57th Street
        unit:
          type: string
          nullable: true
          example: '#48B'
        city:
          type: string
          example: NEW YORK
        state:
          type: string
          example: NY
        zipCode:
          type: string
          example: '10019'
    GeoPoint:
      type: object
      properties:
        latitude:
          type: number
          format: double
          example: 40.7654
        longitude:
          type: number
          format: double
          example: -73.9791
    ListingSummary:
      type: object
      properties:
        id:
          type: string
        address:
          $ref: '#/components/schemas/Address'
        price:
          type: integer
        netEffective:
          type: integer
          nullable: true
        monthsFree:
          type: integer
          nullable: true
        leaseTermMonths:
          type: integer
          nullable: true
        bedroomCount:
          type: integer
          nullable: true
        fullBathroomCount:
          type: integer
          nullable: true
        halfBathroomCount:
          type: integer
          nullable: true
        buildingType:
          type: string
          nullable: true
          enum:
            - CONDO
            - CO_OP
            - RENTAL
            - TOWNHOUSE
            - MULTI_FAMILY
            - HOUSE
            - TWOFAMILY
            - THREEFAMILY
            - MIXED_USE
        areaName:
          type: string
          nullable: true
        areaId:
          type: integer
          nullable: true
        status:
          type: string
          enum:
            - ACTIVE
            - IN_CONTRACT
            - OFF_MARKET
        geoPoint:
          type: object
          allOf:
            - $ref: '#/components/schemas/GeoPoint'
          nullable: true
        leadPhotoKey:
          type: string
          nullable: true
          description: >-
            32-char hex key. Construct URL:
            `https://photos.zillowstatic.com/fp/{key}-se_large_800_400.webp`
        sourceGroupLabel:
          type: string
          nullable: true
          description: Brokerage name
        urlPath:
          type: string
        listingType:
          type: string
          enum:
            - rental
            - sale
    Area:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        level:
          type: integer
          description: 0=borough, 1=sub-borough, 2=neighborhood
        parentId:
          type: integer
          nullable: true
        slug:
          type: string
          nullable: true
        encodedBoundary:
          type: string
          nullable: true
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          required:
            - code
            - message
            - status
          properties:
            code:
              type: string
            message:
              type: string
            status:
              type: integer
            retryAfter:
              type: integer
              nullable: true
