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

# Get search status

> Poll a v3 people search, track each phase, and use results as they become ready.

Returns the latest search snapshot. Polling this endpoint advances pending discovery and profile work before returning the response.

```bash theme={"dark"}
curl "https://api.orbitsearch.com/v3/search/$SEARCH_ID" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

| Parameter   | Location | Required | Description                                    |
| ----------- | -------- | -------- | ---------------------------------------------- |
| `search_id` | path     | Yes      | The `search_id` returned by `POST /v3/search`. |

The response has the same shape as [Start a search](/api/search/search). Every result includes `sources` containing `search`, `candidate_discovery`, or both. Known-search results retain their lightweight `preview` on every poll, regardless of `include_profile`. When `include_profile` is `true`, every poll also embeds the latest readable canonical profile, including a level 1 profile while that result is still `enriching`.

## Know when a search is done

`status` answers one question: is the whole search done, and how did it go? Continue polling while it is `running`. Stop when it becomes:

* `completed`: every result reached the requested `profile_depth`. A search can also complete with an empty `results` list when nothing matched.
* `completed_with_errors`: ready results are available, but at least one result or candidate discovery failed.
* `failed`: work failed and the search has no ready results.

A search completes when every result is published at the requested depth. Orbit can continue to improve published profiles afterward, so a later profile read can return more data than the search response embedded.

## Track each phase

Two snapshot fields report the search's phases independently:

* `candidate_discovery_completed` answers: is the result list final? When `true`, discovery adds no more results to this search. For discovery searches it becomes `true` as soon as clustering fixes the candidate set and every discovered person appears in `results` — usually while those results are still `generating`. Present only when the search ran with `candidate_discovery: true`. If discovery failed, `candidate_discovery_failure` explains why.
* `profile_upgrades_completed` answers: are the current profiles final? When `true`, every result currently in `results` reached the requested `profile_depth` or failed. While `candidate_discovery_completed` is `false`, discovery can still add results that bring new profile work.

Poll on `status` alone — it is the only signal that the search is finished. Use these fields to show progress. For a discovery search, `candidate_discovery_completed: true` with `profile_upgrades_completed: false` is the normal mid-run state: all people are found and their profiles are still building, so you can render the final list of people while their profiles fill in.

## Result statuses

Each entry in `results` moves through its own lifecycle, separate from the search `status`:

| Status       | Meaning                                                                                                                                                                                                                                                                                                                                |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `generating` | Candidate discovery found this person and is building their first profile. No readable profile exists yet and `generation_level` is `null`. The result can already carry `preview` and `candidate_sources`. This status appears only on candidate discovery results, so searches without `candidate_discovery: true` never produce it. |
| `enriching`  | A profile exists and can be read at its current depth, but it is below the requested `profile_depth`. Orbit is building it up.                                                                                                                                                                                                         |
| `ready`      | The profile reached the requested `profile_depth`. With `include_profile: true`, the result embeds the `profile` object.                                                                                                                                                                                                               |
| `failed`     | This result cannot reach the requested depth. `failure` contains a machine-readable reason. A failed result does not block other results.                                                                                                                                                                                              |

Statuses only move forward: `generating` becomes `enriching`, `enriching` becomes `ready` or `failed`, and `ready` never regresses.

## Result stability

Snapshots are stable between polls:

* A result never disappears from a later response. New candidate results append after the existing rows.
* `limit` caps only the initial matches from the existing Orbit index. Candidate Discovery results append outside that quota, and profile upgrades update rows in place. The final `results` count can therefore exceed `limit` and has no fixed multiple of it.

Key your UI by `profile_id` and update rows in place. You can render `ready` results while the rest of the search is still running.

```javascript theme={"dark"}
async function waitForSearch(searchId) {
  while (true) {
    const response = await fetch(
      `https://api.orbitsearch.com/v3/search/${searchId}`,
      { headers: { Authorization: `Bearer ${process.env.ORBIT_API_KEY}` } }
    );
    const search = await response.json();

    if (search.status !== "running") return search;
    await new Promise((resolve) => setTimeout(resolve, 1500));
  }
}
```

During candidate discovery, the polling response includes pending and assigned source receipts:

```json theme={"dark"}
{
  "discovered_sources": [
    {
      "link": "https://example.org/ml-engineers",
      "title": "Machine learning engineers",
      "sourceImage": "https://example.org/favicon.ico"
    }
  ],
  "results": [
    {
      "profile_id": "profile_456",
      "status": "enriching",
      "generation_level": 1,
      "sources": ["candidate_discovery"],
      "candidate_sources": [
        {
          "link": "https://example.com/team",
          "title": "Example team",
          "sourceImage": "https://example.com/favicon.ico"
        }
      ]
    },
    {
      "profile_id": "profile_789",
      "status": "generating",
      "generation_level": null,
      "sources": ["candidate_discovery"],
      "preview": {
        "id": "profile_789",
        "displayName": "Example Candidate",
        "city": "San Francisco"
      }
    }
  ]
}
```

`discovered_sources` starts filling within seconds of the search starting, while Orbit is still gathering, and decreases as Orbit assigns sources to candidates. The assigned receipts appear in `results[].candidate_sources` and remain there when the result becomes ready. Every receipt contains `link`, `title`, and `sourceImage`. `results[].sources` is the existing result-origin list and does not contain source receipts.

In both receipt arrays, `link` is the URL of the discovered source page, `title` is the display title of the source page or site, and `sourceImage` is the URL of the favicon for the site that hosts the source page.

This endpoint requires `search:read`. If the original search used `include_profile: true`, the caller also needs `profile:read`. A search is only visible to the API key that created it.


## OpenAPI

````yaml openapi.json GET /v3/search/{search_id}
openapi: 3.1.0
info:
  title: Orbit API
  version: 3.0.0
  description: Search for people and enrich known Orbit profiles.
servers:
  - url: https://api.orbitsearch.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Search
    description: Find people and poll search results.
  - name: Enrich
    description: Read or enrich known Orbit profiles.
  - name: Watchers
    description: Watch a profile on a schedule and read what each run found.
  - name: Webhooks
    description: Register endpoints that receive signed event deliveries.
paths:
  /v3/search/{search_id}:
    get:
      tags:
        - Search
      summary: Get search status
      description: >-
        Return the latest search snapshot. Poll while the status is `running`.
        Requires the `search:read` scope. Searches that include profiles also
        require `profile:read`.
      operationId: getPeopleSearch
      parameters:
        - $ref: '#/components/parameters/SearchId'
      responses:
        '200':
          description: The current search snapshot.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchSnapshot'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    SearchId:
      name: search_id
      in: path
      required: true
      description: The search ID returned by `POST /v3/search`.
      schema:
        type: string
        minLength: 1
  schemas:
    SearchSnapshot:
      type: object
      required:
        - search_id
        - request_id
        - status
        - candidate_discovery
        - profile_depth
        - include_profile
        - profile_upgrades_completed
        - results
        - created_at
        - updated_at
        - links
      properties:
        search_id:
          type: string
          description: >-
            Server-generated identifier for this search. Use it to poll the
            status endpoint.
        request_id:
          type: string
          description: >-
            The idempotency key from the original request, or a generated value
            when the request omitted it.
        status:
          type: string
          description: >-
            Overall search lifecycle. `running` means work is still in progress;
            poll until the status is terminal. `completed` means every result
            reached the requested profile depth; a search can complete with an
            empty results list when nothing matched. `completed_with_errors`
            means ready results are available but some work failed. `failed`
            means work failed and no ready results are available.
          enum:
            - running
            - completed
            - completed_with_errors
            - failed
        candidate_discovery:
          type: boolean
          description: >-
            Whether this search runs candidate discovery, as resolved from the
            request.
        candidate_discovery_completed:
          type: boolean
          description: >-
            Whether the result list is final. Once true, discovery adds no more
            results to this search; the results themselves can still be
            generating or enriching toward the requested depth. For discovery
            searches this becomes true when clustering completes and every
            discovered person appears in results. If discovery failed,
            candidate_discovery_failure explains why. Present only when
            candidate_discovery is true.
        profile_depth:
          type: string
          description: The minimum profile depth requested for each ready result.
          enum:
            - partial
            - full
        include_profile:
          type: boolean
          description: Whether ready results embed the full profile object.
        profile_upgrades_completed:
          type: boolean
          description: >-
            Whether every result in results has reached the requested
            profile_depth or failed. While candidate_discovery_completed is
            false, discovery can still add results with new profile work.
        results:
          type: array
          description: >-
            The people this search has found so far. Results are append-only: a
            reported result is never removed, and a ready result never
            regresses.
          items:
            $ref: '#/components/schemas/SearchResult'
        discovered_sources:
          type: array
          description: >-
            Candidate Discovery source receipts that have not yet been assigned
            to a result. Present when candidate_discovery is true.
          items:
            $ref: '#/components/schemas/CandidateDiscoverySource'
        candidate_discovery_failure:
          $ref: '#/components/schemas/Failure'
          description: Why candidate discovery failed. Present only when discovery failed.
        created_at:
          type: string
          format: date-time
          description: When the search was created.
        updated_at:
          type: string
          format: date-time
          description: When the snapshot last changed.
        links:
          type: object
          required:
            - status
          properties:
            status:
              type: string
              description: Relative URL for the search status endpoint.
    SearchResult:
      type: object
      required:
        - profile_id
        - status
        - generation_level
        - sources
      properties:
        profile_id:
          type: string
          description: >-
            Stable identifier for this person. Use it with the profile
            endpoints.
        status:
          type: string
          description: >-
            Result lifecycle. `generating` means candidate discovery found this
            person but no readable profile exists yet; it appears only on
            candidate discovery results. `enriching` means a readable profile
            exists but is below the requested depth. `ready` means the profile
            reached the requested profile_depth. `failed` means this result
            cannot reach the requested depth; failure explains why. Statuses
            only move forward.
          enum:
            - generating
            - enriching
            - ready
            - failed
        generation_level:
          description: >-
            The stored generation level of this profile: 1 contains Orbit
            identity data, 2 adds LinkedIn enrichment, 3 adds web, social, and
            OSINT enrichment. A `partial` depth requires at least level 2 and a
            `full` depth requires level 3. Null until the profile is first
            published.
          oneOf:
            - type: integer
              minimum: 1
            - type: 'null'
        sources:
          type: array
          description: >-
            The origins that produced this result. This field does not contain
            source receipts.
          items:
            type: string
            enum:
              - search
              - candidate_discovery
        candidate_sources:
          type: array
          description: >-
            Candidate Discovery source receipts assigned to this result.
            Assigned receipts remain stable when the result becomes ready.
          items:
            $ref: '#/components/schemas/CandidateDiscoverySource'
        preview:
          type: object
          additionalProperties: true
        profile:
          $ref: '#/components/schemas/PublicProfile'
        failure:
          $ref: '#/components/schemas/Failure'
    CandidateDiscoverySource:
      type: object
      additionalProperties: false
      required:
        - link
        - title
        - sourceImage
      properties:
        link:
          type: string
          format: uri
          description: URL of the discovered source page.
        title:
          type: string
          description: Display title of the source page or site.
        sourceImage:
          type: string
          format: uri
          description: URL of the favicon for the site that hosts the source page.
    Failure:
      type: object
      required:
        - code
        - message
        - retryable
        - reason
      properties:
        code:
          type: string
        message:
          type: string
        retryable:
          type: boolean
        reason:
          type: string
        suggested_inputs:
          type: array
          items:
            type: string
    ErrorResponse:
      type: object
      required:
        - status
        - error
      properties:
        status:
          type: string
          enum:
            - failed
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
    PublicProfile:
      type: object
      description: >-
        The public Orbit profile. Available fields depend on the profile
        generation level.
      required:
        - id
        - generationLevel
        - avatarUrl
        - verified
        - sections
        - sources
      additionalProperties: true
      properties:
        id:
          type: string
        displayName:
          type: string
        personName:
          type: string
        aliases:
          type: array
          items:
            type: string
        generationLevel:
          oneOf:
            - type: integer
              minimum: 1
              maximum: 3
            - type: 'null'
        avatarUrl:
          oneOf:
            - type: string
              format: uri
            - type: 'null'
        profileUrl:
          type: string
          format: uri
        slug:
          type: string
        category:
          type: object
          required:
            - id
            - label
          properties:
            id:
              type: string
            label:
              type: string
        verified:
          type: boolean
        location:
          type: object
          properties:
            city:
              type: string
            region:
              type: string
            country:
              type: string
        headline:
          type: object
          properties:
            jobTitle:
              type: string
            companyName:
              type: string
            schoolName:
              type: string
        emails:
          type: array
          items:
            type: string
            format: email
        phoneNumbers:
          type: array
          items:
            type: string
        addresses:
          type: array
          items:
            type: object
            additionalProperties: true
        sections:
          type: object
          description: >-
            Structured profile sections. Standard sections are objects with
            section-specific items, or null when no public data is available.
            Items can carry item-level sources that map each item to its
            supporting evidence.
          additionalProperties:
            oneOf:
              - type: 'null'
              - type: object
                description: >-
                  A content section: items plus an optional deduplicated
                  section-level sources union.
                required:
                  - items
                additionalProperties: true
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                      properties:
                        sources:
                          type: array
                          description: >-
                            Public evidence for this item only. Each source can
                            include excerpt chunks that support the item.
                            Omitted when no evidence is attributed to the item.
                          items:
                            $ref: '#/components/schemas/Source'
                  sources:
                    type: array
                    description: Deduplicated union of the evidence for the whole section.
                    items:
                      $ref: '#/components/schemas/Source'
              - type: object
                description: >-
                  The bio section: a public profile summary with optional
                  sources instead of items.
                required:
                  - bio
                additionalProperties: true
                properties:
                  bio:
                    type: string
                  sources:
                    type: array
                    description: Deduplicated union of the evidence for the whole section.
                    items:
                      $ref: '#/components/schemas/Source'
        sources:
          type: array
          description: Deduplicated and sanitized public sources used across the profile.
          items:
            $ref: '#/components/schemas/Source'
    Source:
      type: object
      description: >-
        A sanitized public source. Profile-level sources, section sources, item
        sources, and image sources share this shape.
      required:
        - link
      additionalProperties: true
      properties:
        link:
          type: string
          format: uri
        title:
          type: string
        summary:
          type: string
        caption:
          type: string
        images:
          type: array
          items:
            type: string
            format: uri
        sourceName:
          type: string
        sourceImage:
          type: string
          format: uri
        chunks:
          type: array
          description: Public evidence text extracted from the source.
          items:
            type: object
            required:
              - text
            properties:
              text:
                type: string
        sources:
          type: array
          description: >-
            Nested public sources when one source record contains additional
            sources.
          items:
            $ref: '#/components/schemas/Source'
  responses:
    BadRequest:
      description: The request is not valid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: The API key is missing or not valid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: The API key does not have the required scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: The requested resource was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: The API key exceeded a rate limit.
      headers:
        Retry-After:
          description: Seconds to wait before another request.
          schema:
            type: integer
            minimum: 0
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Orbit API key
      description: Use an Orbit API key from the developer dashboard.

````