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

# Find best-fit practitioners nearby

> Returns nearby practitioners, ranked by how good a fit they are for a given referral target.

Returns nearby practitioners, ranked by how good a fit they are for a given referral target.

Each result pairs a practitioner with one of their practice locations:

* **Practitioner**: name, NPI number, and `referralScopeMatch`, our estimate of whether their billing history fits the referral target
* **Location**: address and coordinates, `staleAffiliationRisk`, our estimate of whether they are still seeing patients there, and `bookability`, our estimate of whether an appointment can be booked there
* **Distance**: straight-line (not driving) miles from the coordinates you searched
* **Score**: from 0 to 100, how good a fit this practitioner, at this location, is for the referral you searched; higher is better

## Ranking

`score` combines fit (`referralScopeMatch`), distance, and whether an appointment can be booked at the location (`bookability`). Ties are broken consistently, so repeating a search returns the same order.

Results are not filtered by fit: poor fits are still returned, ranked below better ones. Filter on `practitioner.referralScopeMatch` if you only want likely matches.

Scores are on a fixed scale, so they can be compared across searches that report the same `servedModels`. When `servedModels` has no `practitionerLocationRisk`, scores are computed without a bookability adjustment and locations omit both `staleAffiliationRisk` and `bookability`. Locations without a `bookability` label, such as those of practitioners the model does not cover, receive no adjustment.

## Pagination

When more results are available, the response includes `nextCursor`. To get the next page, repeat the request with `cursor` set to that value. Stop when `nextCursor` is absent.

A cursor is only valid for the search that produced it: changing any other parameter returns a `400`.

<Warning>
  A non-match (`unlikely_match` or `very_unlikely_match`) means a routine referral target does not fit the provider's observed billing. It does not mean the service is outside their scope of practice or that they are unqualified; it reflects what they routinely bill, not what they are licensed or trained to do.
</Warning>

<Info>
  Geocoding data is provided in part by OpenStreetMap. © OpenStreetMap contributors, available under the [Open Database License (ODbL)](https://opendatacommons.org/licenses/odbl/). Additional data is derived from the U.S. Census Bureau TIGER/Line® Shapefiles (public domain), from other public sources, and from commercial sources, such as Geocodio.
</Info>


## OpenAPI

````yaml https://api.perfectreferral.com/v1/openapi.yml get /search-by-target
openapi: 3.1.0
info:
  version: 1.1.0
  title: Perfect Referral
  description: Refer patients to the right specialist the first time.
  contact:
    email: support@threshold.health
servers:
  - url: https://api.perfectreferral.com/v1
security:
  - bearerHttpAuthentication: []
tags:
  - name: Model
  - name: Practitioner
  - name: Practitioner Location
  - name: Specialization
  - name: Search
paths:
  /search-by-target:
    get:
      tags:
        - Search
      summary: Find best-fit practitioners nearby
      description: >-
        Returns nearby practitioners, ranked by how good a fit they are for a
        given referral target.
      operationId: searchByTarget
      parameters:
        - name: referralTargetId
          in: query
          required: true
          description: >-
            The referral target to rank practitioners against, from `GET
            /specializations/{id}/targets`. Target ids change between model
            versions, so look them up rather than storing them.
          schema:
            type: string
            format: uuid
        - name: latitude
          in: query
          required: true
          description: Latitude of the point to search around, in WGS84 decimal degrees.
          schema:
            type: number
            format: double
            minimum: -90
            maximum: 90
        - name: longitude
          in: query
          required: true
          description: Longitude of the point to search around, in WGS84 decimal degrees.
          schema:
            type: number
            format: double
            minimum: -180
            maximum: 180
        - name: radiusMiles
          in: query
          required: true
          description: How far from the coordinates to search, in straight-line miles.
          schema:
            type: integer
            minimum: 1
            maximum: 25
        - name: pageSize
          in: query
          required: false
          description: How many results to return per page.
          schema:
            type: integer
            default: 25
            minimum: 1
            maximum: 100
        - name: cursor
          in: query
          required: false
          description: >-
            The `nextCursor` from the previous page. Send it with the same
            parameters as the original request.
          schema:
            type: string
      responses:
        '200':
          description: Expected response to a valid request
          headers:
            X-Ratelimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-Ratelimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            X-Ratelimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PractitionerTargetSearchResult'
        '400':
          description: The request was malformed.
          headers:
            X-Ratelimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-Ratelimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            X-Ratelimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                code: '400'
                message: Invalid id format
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        5XX:
          $ref: '#/components/responses/ServerError'
      security:
        - bearerHttpAuthentication: []
components:
  headers:
    RateLimitRemaining:
      description: Requests left in the current monthly quota window.
      schema:
        type: integer
    RateLimitReset:
      description: Unix time (seconds) when the monthly quota window resets.
      schema:
        type: integer
    RateLimitLimit:
      description: Total requests allowed per monthly quota window.
      schema:
        type: integer
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
  schemas:
    PractitionerTargetSearchResult:
      type: object
      required:
        - practitionerLocations
        - servedModels
      properties:
        practitionerLocations:
          type: array
          description: >-
            Results, ordered best-first by `score`. Empty when we have no
            estimate of fit for any practitioner within the radius.
          items:
            $ref: '#/components/schemas/PractitionerTargetSearchPractitionerLocation'
        nextCursor:
          type: string
          description: >-
            Opaque cursor for the next page, to be passed back as the `cursor`
            query parameter. Absent when there are no additional results.
        servedModels:
          type: object
          description: The version of each model whose output appears in this response.
          required:
            - referralScopeMatch
          properties:
            referralScopeMatch:
              type: string
              description: >-
                The version that produced the `referralScopeMatch` labels and
                the ranking of `practitionerLocations`.
            practitionerLocationRisk:
              type: string
              description: >-
                The version that produced the `staleAffiliationRisk` and
                `bookability` labels on `practitionerLocations[].location`.
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: HTTP status error code.
        message:
          type: string
          description: Human-readable error description.
    PractitionerTargetSearchPractitionerLocation:
      type: object
      required:
        - practitioner
        - location
        - distanceMiles
        - score
      properties:
        practitioner:
          $ref: '#/components/schemas/PractitionerTargetSearchPractitioner'
        location:
          $ref: '#/components/schemas/PractitionerTargetSearchLocation'
        distanceMiles:
          type: number
          format: double
          minimum: 0
          description: >-
            Straight-line distance from the searched coordinates to this
            location, in miles.
        score:
          type: integer
          description: >-
            How good a fit this practitioner, at this location, is for the
            referral you searched, from 0 to 100, higher being better. Combines
            the practitioner's `referralScopeMatch`, the distance, and the
            location's `bookability`. Comparable only across searches that
            report the same `servedModels`.
          minimum: 0
          maximum: 100
    PractitionerTargetSearchPractitioner:
      type: object
      required:
        - firstName
        - lastName
        - npiNumber
        - referralScopeMatch
      properties:
        firstName:
          type: string
          description: The practitioner's given name.
        lastName:
          type: string
          description: The practitioner's family name.
        npiNumber:
          type: string
          description: >-
            The individual (NPI-1) National Provider Identifier this result was
            matched on. Pass it to `GET /practitioners/{npiNumber}` for the
            practitioner's full record.
        referralScopeMatch:
          $ref: '#/components/schemas/ReferralScopeMatchLabel'
    PractitionerTargetSearchLocation:
      type: object
      required:
        - address
        - geocoding
      properties:
        address:
          $ref: '#/components/schemas/Address'
        geocoding:
          $ref: '#/components/schemas/Geocoding'
        staleAffiliationRisk:
          $ref: '#/components/schemas/StaleAffiliationRisk'
        bookability:
          $ref: '#/components/schemas/Bookability'
    ReferralScopeMatchLabel:
      type: string
      description: >
        Our estimate of whether the practitioner is an appropriate match for the
        target, based on their observed billing. Predictive values are
        calibrated against an even sampling of held-out providers in the
        target's specialty.


        - `very_likely_match`: strongly indicates a match (at least 95%
        predictive value).

        - `likely_match`: indicates a match (at least 80% predictive value).

        - `uncertain`: evaluable, but the evidence supports neither a match nor
        a no-match call.

        - `unlikely_match`: indicates this is not a match (at least 80%
        predictive value).

        - `very_unlikely_match`: strongly indicates this is not a match (at
        least 95% predictive value).

        - `insufficient_data`: not enough billing data to evaluate.


        `uncertain` and `insufficient_data` are not negative signals; treat both
        as a "no confident call." `uncertain` means the evidence was mixed;
        `insufficient_data` means there was too little billing data to judge.
      enum:
        - very_likely_match
        - likely_match
        - uncertain
        - unlikely_match
        - very_unlikely_match
        - insufficient_data
    Address:
      type: object
      required:
        - line1
        - city
        - state
        - postalCode
      properties:
        line1:
          type: string
          description: >-
            The first line of the street address, typically the street number
            and name.
        line2:
          type: string
          description: >-
            The second line of the street address, when present, such as a
            suite, unit, or floor.
        city:
          type: string
          description: The city name.
        state:
          type: string
          description: The two-letter USPS abbreviation for the state or territory.
        postalCode:
          type: string
          description: The postal (ZIP) code, in the five digit form.
    Geocoding:
      type: object
      required:
        - latitude
        - longitude
      description: Geocoding data for the location's address, if available.
      properties:
        latitude:
          type: number
        longitude:
          type: number
    StaleAffiliationRisk:
      type: object
      required:
        - label
      description: >-
        Stale affiliation risk data for this practitioner–location pair, if
        available.
      properties:
        label:
          type: string
          description: >
            Our estimate of whether the practitioner is actually seeing patients
            at this location, based on public sources such as monthly Medicare
            billing histories, NPPES registry data, and health plan network
            directories, and trained on independently verified
            practitioner–location pairs. Each label covers a fixed range of
            probability that the practitioner is seeing patients at this
            location:


            - `very_likely_active`: strongly indicates the practitioner is
            seeing patients at this location (85–100%).

            - `likely_active`: indicates the practitioner is seeing patients at
            this location (65–85%).

            - `uncertain`: the evidence supports neither an active nor an
            inactive call (35–65%).

            - `likely_inactive`: indicates the practitioner is not seeing
            patients at this location (15–35%).

            - `very_likely_inactive`: strongly indicates the practitioner is not
            seeing patients at this location — the listing is stale or was never
            patient-facing (0–15%).


            Ranges are measured on independently verified, randomly sampled
            listings from our national set of physician listings; in another
            directory, the order of the labels holds but the rates can shift.
            See the [stale location
            methodology](https://docs.perfectreferral.com/methodology/stale-location)
            for measured rates, sources, and limitations. Predictions cover
            physicians (MD and DO) only; locations of other practitioners carry
            no label.
          enum:
            - very_likely_active
            - likely_active
            - uncertain
            - likely_inactive
            - very_likely_inactive
    Bookability:
      type: object
      required:
        - label
      description: Bookability data for this practitioner–location pair, if available.
      properties:
        label:
          type: string
          description: >
            Our estimate of whether a patient can book an appointment with this
            practitioner at this location. Each label covers a fixed range of
            probability that an appointment can be booked:


            - `very_likely_bookable`: strongly indicates an appointment can be
            booked (85–100%).

            - `likely_bookable`: indicates an appointment can be booked
            (65–85%).

            - `uncertain`: the evidence supports neither a bookable nor a
            not-bookable call (35–65%).

            - `likely_not_bookable`: indicates an appointment cannot be booked
            (15–35%).

            - `very_likely_not_bookable`: strongly indicates an appointment
            cannot be booked (0–15%).


            Distinct from `staleAffiliationRisk`: a practitioner can be actively
            seeing patients at a location where patients cannot book them, such
            as a hospital where they work as a hospitalist or emergency
            physician, or an operating room where they perform procedures booked
            through a clinic elsewhere. Bookability and stale affiliation risk
            come from one model, so a location is never labeled more bookable
            than it is active. Bookability does not indicate whether the
            practitioner is accepting new patients, participates in a given
            plan, or how soon they can be seen.


            Ranges are measured the same way as for stale affiliation risk; see
            the [stale location
            methodology](https://docs.perfectreferral.com/methodology/stale-location).
            Predictions cover physicians (MD and DO) only; locations of other
            practitioners carry no label.
          enum:
            - very_likely_bookable
            - likely_bookable
            - uncertain
            - likely_not_bookable
            - very_likely_not_bookable
  responses:
    Unauthorized:
      description: API token missing or invalid.
      headers:
        X-Ratelimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-Ratelimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        X-Ratelimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: '401'
            message: Unauthorized
    Forbidden:
      description: >-
        The caller cannot access a model version required to serve this request:
        either the requested version is above their tier, or no version of a
        required model is available to them.
      headers:
        X-Ratelimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-Ratelimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        X-Ratelimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: '403'
            message: pro tier required to access this model
    TooManyRequests:
      description: >-
        Too many requests. Returned when either the monthly request quota or the
        per-second request-rate limit is exceeded; the `message` field
        distinguishes them ("rate limit exceeded" is the monthly quota, "request
        rate exceeded" is the per-second limit).
      headers:
        X-Ratelimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-Ratelimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        X-Ratelimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: '429'
            message: Too Many Requests
    InternalError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: '500'
            message: Unexpected server error
    ServerError:
      description: Server error.
  securitySchemes:
    bearerHttpAuthentication:
      description: >-
        Bearer authentication header of the form `Bearer <token>`, where
        `<token>` is your API token.
      type: http
      scheme: bearer

````

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