API reference navigation
POST /v1/searches/run

Run a search plan

Access
Authenticated
search:read
Cost
1 credit per result returned
Rate limit
30 / minute per account
Quota
None

Run a query plan directly against the talent corpus and get back result skeletons. A plan is 1 to 4 lanes run in parallel (semantic search over repositories, BM25 over repositories or user text, an attribute-ranked user lane, semantic search over LinkedIn profiles), fused by reciprocal rank: someone surfaced by two lanes ranks above someone surfaced by one, and there are no weights to tune because no weighting the index cannot execute is offered. Billed 1 credit per skeleton ACTUALLY RETURNED; limit is the price ceiling and a run that returns nothing costs nothing. A skeleton is identity plus evidence: name, handle, avatar, headline, location, devrank tier and followers, and the repositories that matched with the reason they matched. Everything richer (contribution garden, AI summaries, verified email, LinkedIn detail) is a facet bought per developer on POST /v1/developers, which is unchanged. There is no cursor: pass the ids you have already seen in excludeDeveloperIds. Counts are never exact, because an exact count over this corpus cannot be computed inside a request. Freshness is disclosed rather than assumed: asOf carries the crawl time on the profile, devrank is a weekly batch, and lastSeen is an approximation from repository crawl times. Send a natural-language query instead of a plan to have the plan written for you: planning is free and cached, the plan that ran is echoed back with its caveats, and only the rows bill.

Request body RunSearchRequest

  • plan object

    The query plan to run: 1-4 parallel lanes, shared user filters, RRF fusion.

    • intent string required
      max 300 chars
    • lanes array of one of required
      min 1 items, max 4 items
      Option 1
      • kind string required
        users.bm25
      • text string required
        min 1 chars, max 200 chars
      Option 2
      • kind string required
        users.rank
      • attribute string required
        createdAt
      • direction string required
        ascdesc
      Option 3
      • kind string required
        repos.ann
      • text string required
        min 1 chars, max 500 chars
      • repoFilters array of array
        max 6 items
      Option 4
      • kind string required
        repos.bm25
      • text string required
        min 1 chars, max 200 chars
      • repoFilters array of array
        max 6 items
      Option 5
      • kind string required
        linkedin.ann
      • text string required
        min 1 chars, max 500 chars
      • liFilters array of array
        max 6 items
      Option 6
      • kind string required
        users.ann
      • text string required
        min 1 chars, max 500 chars
    • userFilters array of array default: []
      max 8 items
    • post object
      • minDevrankTier string
        EliteRareSolid
    • fuse object default: {"method":"rrf","k":60}
      • method string required
        rrf
      • k integer default: 60
        min 1, max 200
  • query string

    Natural language, planned server-side and then run. Planning is free and cached; you are still billed only for the rows returned. Send `plan` instead when you want to control the plan yourself. Exactly one of the two.

    max 20000 chars
  • limit integer default: 25

    Maximum skeletons to return, and therefore the price ceiling: you are billed 1 credit per row actually returned, so a run that yields nothing costs nothing.

    min 1, max 100
  • excludeDeveloperIds array of string default: []

    Ids to keep out of this run. This is how "get me more" works — there is no cursor; the ids compile to a native NotIn filter.

    max 5000 items

Responses

  • 200 The result skeletons, the plan that ran, and its bill
    • results array of object required
      max 100 items
      • skeleton SearchSkeleton required
        • id string required

          The BountyLab user id (github-users-v3.id).

          max 64 chars
        • login string required
          max 200 chars
        • name string required
          max 200 chars
        • avatarUrl string required
          max 2048 chars
        • headline string | null required
          max 2048 chars
        • location string | null required
          max 200 chars
        • country string | null required
          max 200 chars
        • links object required
          • github string required
            max 2048 chars
          • linkedin string | null required
            max 2048 chars
        • cracked boolean required
        • devrank object | null required
          • tier string required
            max 200 chars
          • score number required
          • asOf string | null required

            When the devrank row was computed. Always null today: users_elo carries no timestamp column. The ceiling is the weekly batch, so treat this as up to 7 days old.

            max 200 chars
        • followers number | null required
        • matchedRepos array of SearchSkeletonRepo required
          max 100 items
          • fullName string required
            max 200 chars
          • stars number required
          • language string | null required
            max 200 chars
          • why string | null required

            Why this repo is here: the lane and its distance/score against the lane text. Null when the repo is the person’s top work rather than a match.

            max 2048 chars
        • lastSeen string | null required

          APPROXIMATE: the newest crawl time among the matched repos. There is no activity timestamp on a user row, so this is a proxy, not a last-active date.

          max 200 chars
        • score number required

          The fused RRF score. Monotonic within THIS response only.

        • lanes array of string required
          max 100 items
        • asOf object required
          • profile string | null required
            max 200 chars
          • linkedin string | null required
            max 200 chars
    • countStatus object required

      How many matches we can honestly claim. Only ever `atLeast`: an exact count over a 100M+ row corpus times out, so we never run one.

      • kind string required
        atLeast
      • n number required
    • applied object required
      • plan object required
        • intent string required
          max 300 chars
        • lanes array of one of required
          min 1 items, max 4 items
          Option 1
          • kind string required
            users.bm25
          • text string required
            min 1 chars, max 200 chars
          Option 2
          • kind string required
            users.rank
          • attribute string required
            createdAt
          • direction string required
            ascdesc
          Option 3
          • kind string required
            repos.ann
          • text string required
            min 1 chars, max 500 chars
          • repoFilters array of array
            max 6 items
          Option 4
          • kind string required
            repos.bm25
          • text string required
            min 1 chars, max 200 chars
          • repoFilters array of array
            max 6 items
          Option 5
          • kind string required
            linkedin.ann
          • text string required
            min 1 chars, max 500 chars
          • liFilters array of array
            max 6 items
          Option 6
          • kind string required
            users.ann
          • text string required
            min 1 chars, max 500 chars
        • userFilters array of array default: []
          max 8 items
        • post object
          • minDevrankTier string
            EliteRareSolid
        • fuse object default: {"method":"rrf","k":60}
          • method string required
            rrf
          • k integer default: 60
            min 1, max 200
      • warnings array of string required

        What the run cannot honestly promise. Empty when you sent your own plan: these are the planner’s caveats about a natural-language `query`.

        max 100 items
      • scope string required

        `linkedin_linked_subset` whenever a LinkedIn lane contributed: that lane sees ~12% of the corpus, and the UI must say so.

        full_corpuslinkedin_linked_subset
    • billing object required
      • total number required
  • 401 No or invalid credential
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_error
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at the credit top-up).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 403 Missing search:read
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_error
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at the credit top-up).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 422 A lane is not available yet (LANE_UNAVAILABLE), or the query could not be planned (UNPLANNABLE)
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_error
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at the credit top-up).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 502 The planner is unavailable
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_error
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at the credit top-up).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.