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

# Search Discover listings

> Searches the published Discover listings the way Cal.ai does. In-person results stay within the area you name, else around your saved ZIP code; with neither, nothing is searched and `areaNeeded` is returned. When the named area or the saved ZIP code cannot be found, nothing is searched and `areaNeeded` comes with `areaNotFound`. Online-only categories and `onlineOnly` searches are searched everywhere. An empty neighborhood widens to its whole region (`widenedFrom`); an empty search retries with the category alone (`broadenedFrom`); when the whole area has nothing, the web is searched there once and businesses not on Discover come back in `webBusinesses`. Availability is judged in your saved time zone. Searches are limited to 20 per minute per user, and web searches to 10 per hour per user, each shared across all of the user's API keys and access tokens; past the web search limit a search still returns its listings, without `webBusinesses`. The web search runs on your own AI access: when one of your organizations has turned AI off, it is skipped and the listings still return. A place lookup that is rate limited answers 429 and one that fails answers 503, each with a message starting `Place lookup`; neither means the place does not exist. Other 429s are the request limits above. Each result's `bookingUrl` is attributed to Discover. If accessed using an OAuth access token, the `EVENT_TYPE_READ` scope is required.



## OpenAPI

````yaml /api-reference/v2/openapi.json get /v2/discover/listings/search
openapi: 3.0.0
info:
  title: Cal.com API v2
  description: ''
  version: 1.0.0
  contact: {}
servers: []
security: []
tags: []
paths:
  /v2/discover/listings/search:
    get:
      tags:
        - Discover
      summary: Search Discover listings
      description: >-
        Searches the published Discover listings the way Cal.ai does. In-person
        results stay within the area you name, else around your saved ZIP code;
        with neither, nothing is searched and `areaNeeded` is returned. When the
        named area or the saved ZIP code cannot be found, nothing is searched
        and `areaNeeded` comes with `areaNotFound`. Online-only categories and
        `onlineOnly` searches are searched everywhere. An empty neighborhood
        widens to its whole region (`widenedFrom`); an empty search retries with
        the category alone (`broadenedFrom`); when the whole area has nothing,
        the web is searched there once and businesses not on Discover come back
        in `webBusinesses`. Availability is judged in your saved time zone.
        Searches are limited to 20 per minute per user, and web searches to 10
        per hour per user, each shared across all of the user's API keys and
        access tokens; past the web search limit a search still returns its
        listings, without `webBusinesses`. The web search runs on your own AI
        access: when one of your organizations has turned AI off, it is skipped
        and the listings still return. A place lookup that is rate limited
        answers 429 and one that fails answers 503, each with a message starting
        `Place lookup`; neither means the place does not exist. Other 429s are
        the request limits above. Each result's `bookingUrl` is attributed to
        Discover. If accessed using an OAuth access token, the `EVENT_TYPE_READ`
        scope is required.
      operationId: DiscoverListingSearchController_searchListings
      parameters:
        - name: category
          required: false
          in: query
          description: >-
            The Discover category the need maps to, such as `medical` for tooth
            pain.
          schema:
            example: barbers
            type: string
            enum:
              - medical
              - barbers
              - coaching
              - consulting
              - lessons
              - pets
              - sports
              - dance
              - social
              - online-services
              - saas
              - home-services
              - other
        - name: subcategory
          required: false
          in: query
          description: >-
            A subcategory of `category`, such as `dentistry` under `medical`.
            Ignored without its category, and named in `ignoredFilters` when it
            is not one of the category's.
          schema:
            example: haircuts
            type: string
            enum:
              - dentistry
              - therapy
              - physiotherapy
              - nutrition
              - somatic-and-breathwork
              - doula
              - general-care
              - haircuts
              - beard-and-shave
              - coloring
              - kids-cuts
              - nails
              - waxing
              - lashes
              - skin-and-spa
              - massage
              - tattoo
              - life
              - career
              - executive
              - mindset
              - spiritual
              - business
              - tech-and-ai
              - finance-and-tax
              - legal
              - real-estate
              - marketing
              - hr
              - academic
              - language
              - music-and-voice
              - arts
              - chess
              - veterinary
              - grooming
              - training
              - sitting
              - fitness
              - yoga
              - tennis
              - swimming
              - skiing
              - salsa
              - ballet
              - hip-hop
              - ballroom
              - clubs
              - workshops
              - networking
              - tours
              - photoshoots-and-studios
              - pop-ups-and-showrooms
              - design
              - development
              - writing
              - virtual-assistance
              - legal-advice
              - product-demos
              - onboarding
              - customer-support
              - sales
              - cleaning
              - repairs
              - heating-and-cooling
              - inspections
              - auto
        - name: query
          required: false
          in: query
          description: >-
            Free text matched against the title, description, provider and
            subcategory. Never a place.
          schema:
            maxLength: 100
            example: skin fade
            type: string
        - name: neighborhood
          required: false
          in: query
          description: >-
            A neighborhood of a launch region, such as `Capitol Hill`, for
            in-person listings there only. An unknown name comes back in
            `ignoredFilters`.
          schema:
            maxLength: 80
            example: Capitol Hill
            type: string
        - name: onlineOnly
          required: false
          in: query
          description: >-
            Keep only online listings and search them everywhere, whatever
            `area` says. Online searches never fall back to the web.
          schema:
            type: boolean
            example: false
        - name: localQuery
          required: false
          in: query
          description: >-
            For the web fallback, the need in the area's local language when it
            is not English, such as `Zahnarzt` for a dentist in Hamburg.
          schema:
            maxLength: 100
            example: Zahnarzt
            type: string
        - name: area
          required: false
          in: query
          description: >-
            The place to search around: a ZIP code, neighborhood or city.
            Without it, the search uses your saved ZIP code, and answers
            `areaNeeded` when there is none. Pass `anywhere` for results from
            anywhere.
          schema:
            maxLength: 80
            example: '98122'
            type: string
        - name: maximumPrice
          required: false
          in: query
          description: >-
            The highest price, in whole currency units: 50 means $50, 0 means
            free only.
          schema:
            minimum: 0
            example: 50
            type: number
        - name: maximumDurationMinutes
          required: false
          in: query
          description: The longest session, in minutes.
          schema:
            type: number
            minimum: 1
            example: 60
        - name: availableWithin
          required: false
          in: query
          description: >-
            Only listings with a real open slot in this window, in your saved
            time zone. `this_week` means the next 7 days.
          schema:
            example: today
            type: string
            enum:
              - today
              - tomorrow
              - this_week
        - name: Authorization
          in: header
          description: >-
            value must be `Bearer <token>` where `<token>` is api key prefixed
            with cal_, managed user access token, or OAuth access token
          required: true
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchDiscoverListingsOutputResponseDto'
        '400':
          description: A query parameter is not one the search accepts
        '401':
          description: Unauthenticated
        '403':
          description: Insufficient permissions
        '429':
          description: >-
            One of three limits, told apart by the error message: this
            credential's request throttle or this user's limit of 20 searches
            per minute across all of their credentials (both `… Too many
            requests …`), or the place lookup's own limit (`Place lookup rate
            limited …`). Back off and retry later; only the last means the place
            lookup failed.
        '503':
          description: >-
            The place lookup is unavailable for the moment (`Place lookup
            unavailable …`). The place was not rejected; retry the same search
            later.
components:
  schemas:
    SearchDiscoverListingsOutputResponseDto:
      type: object
      properties:
        status:
          enum:
            - success
            - error
          type: string
          example: success
        data:
          $ref: '#/components/schemas/SearchDiscoverListingsData'
      required:
        - status
        - data
    SearchDiscoverListingsData:
      type: object
      properties:
        areaNeeded:
          type: boolean
          example: true
          description: >-
            Present, and true, only when nothing was searched. Without
            `areaNotFound`, no `area` was given and there is no saved ZIP code:
            search again with `area` set. With `areaNotFound`, see that field.
            No search fields are present then.
        areaNotFound:
          type: boolean
          example: true
          description: >-
            Present, and true, only with `areaNeeded`: the named `area`, or your
            saved ZIP code when no `area` was given, could not be found. Search
            again with a nearby city or a ZIP code, not the same place.
        totalMatches:
          type: number
          example: 3
          description: How many published listings matched.
        results:
          description: Up to 8 matching listings, at most 2 per provider.
          type: array
          items:
            $ref: '#/components/schemas/DiscoverListingSearchResultOutput'
        searchedArea:
          type: string
          example: within 10 miles of Seattle, WA
          description: >-
            The area in-person results were kept within; absent for an online or
            `anywhere` search.
        areaSource:
          type: string
          enum:
            - named
            - saved
          example: saved
          description: >-
            Where `searchedArea` came from: `named` when you passed `area` or
            `neighborhood`, `saved` for your saved ZIP code. Absent with
            `searchedArea`.
        ignoredFilters:
          example:
            - neighborhood
          description: >-
            Query parameters the search could not use and ignored, such as an
            unknown `neighborhood`, a `subcategory` outside its `category`, an
            `area` that could not be found, or `availableWithin`.
          type: array
          items:
            type: string
        widenedFrom:
          type: string
          example: Capitol Hill
          description: >-
            The neighborhood that had nothing, when the search widened to its
            whole region.
        broadenedFrom:
          description: >-
            When the search found nothing, it searched again with the category
            alone; these are the filters it dropped.
          allOf:
            - $ref: '#/components/schemas/DiscoverSearchBroadenedFromOutput'
        webBusinesses:
          description: >-
            When no listing in the area matched, businesses found on the public
            web there that are not on Discover, each with an appointment email
            or phone. Absent when the web search is not configured or the search
            is online-only.
          type: array
          items:
            $ref: '#/components/schemas/DiscoverWebBusinessOutput'
    DiscoverListingSearchResultOutput:
      type: object
      properties:
        listingId:
          type: number
          example: 12
          description: The Discover listing id, which `GET /v2/discover/listings` reads.
        title:
          type: string
          example: Skin fade
          description: The service's name.
        description:
          type: string
          nullable: true
          example: A sharp fade with a hot-towel finish.
          description: The listing's description; null when it has none.
        provider:
          type: string
          nullable: true
          example: Fade House
          description: The provider's name; null when the listing shows none.
        avatarUrl:
          type: string
          nullable: true
          example: https://cal.com/api/avatar/fade-house.png
          description: The provider's avatar or team logo URL; null when none.
        category:
          type: string
          enum:
            - MEDICAL
            - BARBERS
            - COACHING
            - CONSULTING
            - LESSONS
            - PETS
            - SPORTS
            - DANCE
            - SOCIAL
            - ONLINE_SERVICES
            - SAAS
            - HOME_SERVICES
            - OTHER
          example: BARBERS
          description: The listing's category.
        subcategory:
          type: string
          nullable: true
          example: haircuts
          description: The listing's subcategory slug; null when none.
        city:
          type: string
          nullable: true
          example: Seattle
          description: The city the listing's card shows; null for an online listing.
        neighborhood:
          type: string
          nullable: true
          example: Capitol Hill
          description: >-
            The listing's neighborhood; null for an online listing or when none
            is known.
        timeZone:
          type: string
          nullable: true
          example: America/Los_Angeles
          description: >-
            The business's IANA time zone, which an in-person slot is shown in;
            null for an online listing.
        latitude:
          type: number
          nullable: true
          example: 47.62
          description: >-
            The listing's public, rounded latitude; null when the business is
            not geocoded.
        longitude:
          type: number
          nullable: true
          example: -122.32
          description: >-
            The listing's public, rounded longitude; null when the business is
            not geocoded.
        distanceMiles:
          type: number
          nullable: true
          example: 1.4
          description: >-
            Miles from the searched area's centre to the listing, to one
            decimal; null without an area or a location.
        durationMinutes:
          type: number
          example: 30
          description: The session's length in minutes.
        priceInCents:
          type: number
          nullable: true
          example: 4500
          description: >-
            The price in the currency's minor unit; null when no price is
            listed, which is not free.
        currency:
          type: string
          example: usd
          description: The price's ISO 4217 currency code.
        price:
          type: string
          example: 45.00 USD
          description: >-
            The price in words: an amount with its currency, `free`, or `not
            listed`.
        rating:
          type: number
          nullable: true
          example: 4.8
          description: The provider's aggregate rating out of 5; null when unrated.
        reviewCount:
          type: number
          nullable: true
          example: 132
          description: How many reviews the rating is based on; null when unrated.
        openNow:
          type: boolean
          example: true
          description: Whether the business is open now.
        verified:
          type: boolean
          example: true
          description: Whether the provider is verified.
        newClientDiscountPercent:
          type: number
          nullable: true
          example: 15
          description: >-
            The discount, in percent, a new client gets on a first visit booked
            through Discover; null when none.
        path:
          type: string
          nullable: true
          example: /discover/fade-house/skin-fade
          description: >-
            The listing's Discover page path on the web app; null when the
            provider has no Discover page.
        bookingUrl:
          type: string
          nullable: true
          example: >-
            https://cal.com/fade-house/skin-fade?utm_source=discover&utm_medium=api&utm_campaign=barbers&utm_content=listing%3A12
          description: >-
            The listing's booking page, attributed to Discover so its new-client
            discount applies; null when the listing has no booking page.
        nextAvailableSlot:
          type: string
          nullable: true
          example: '2026-11-10T17:00:00.000Z'
          description: >-
            The ISO 8601 start of the earliest open slot in the searched window;
            null when none was found.
        nextAvailableSlotLocal:
          type: string
          nullable: true
          example: Today, 9:00 AM PST
          description: >-
            That slot as the listing's card shows it, in `timeZone`, else your
            saved time zone; null when none.
        nextAvailableSlotTimeZone:
          type: string
          nullable: true
          example: PST
          description: >-
            The time zone abbreviation `nextAvailableSlotLocal` is in; null when
            none.
      required:
        - listingId
        - title
        - description
        - provider
        - avatarUrl
        - category
        - subcategory
        - city
        - neighborhood
        - timeZone
        - latitude
        - longitude
        - distanceMiles
        - durationMinutes
        - priceInCents
        - currency
        - price
        - rating
        - reviewCount
        - openNow
        - verified
        - newClientDiscountPercent
        - path
        - bookingUrl
        - nextAvailableSlot
        - nextAvailableSlotLocal
        - nextAvailableSlotTimeZone
    DiscoverSearchBroadenedFromOutput:
      type: object
      properties:
        subcategory:
          type: string
          example: haircuts
          description: The subcategory the search dropped.
        query:
          type: string
          example: skin fade
          description: The free text the search dropped.
    DiscoverWebBusinessOutput:
      type: object
      properties:
        name:
          type: string
          example: Example Barbers
          description: The business's name, from its website.
        url:
          type: string
          example: https://barbers.example/book
          description: The page the business was found on.
        description:
          type: string
          nullable: true
          example: Fades and beard trims
          description: >-
            What the page says the business offers; null when it says nothing
            usable.
        appointmentEmails:
          example:
            - bookings@barbers.example
          description: >-
            Email addresses the business's page offers for making an
            appointment.
          type: array
          items:
            type: string
        appointmentPhones:
          example:
            - (415) 555-0101
          description: Phone numbers the business's page offers for making an appointment.
          type: array
          items:
            type: string
      required:
        - name
        - url
        - description
        - appointmentEmails
        - appointmentPhones

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.