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

# Start a search

> Start a search from a question in plain words. The body has one key, `query`.

The `Accept` request header picks the response:

- `application/json` (the default; `*/*` or no `Accept` header gives the same)
  returns the `search_id` and the state of the search at that moment.
- `text/event-stream` returns the event stream of the search, from its first
  event to `search.finished`. The `X-Search-Id` response header carries the
  `search_id`, so you can resume on `GET /agent/searches/{search_id}/events` if
  the stream breaks.

Error responses are JSON whatever `Accept` says.

A start returns `202` with the new `search_id` and its state, which is `running`.
If you send the same `Idempotency-Key` with the same query again, the response is
`200` with the current state of the first search, and that search can already be
over. Always read `state` from the body, not from the status code. Treat every
state that is not `succeeded`, `failed` or `cancelled` as still in progress,
because a later release adds `queued`.

Only `POST` is allowed on this path. A `GET` returns `405` with
`{"detail": "Method \"GET\" not allowed."}`, and it counts toward the start limit.

The start costs nothing and returns `X-Credits-Used: 0`. The first read after the
search ends charges it. Before a search begins, the API checks that your balance
covers 8 credits for each search of your account that has no charge yet, this one
included. The check takes no credits, and 8 credits is not the price of a search:
a search usually costs much less.

<Note>
    Default `rate-limit` is 6 starts per minute for each account. A request that gets an error counts toward it too. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (the seconds until the limit resets). Send an email to [gtm@crustdata.co](mailto:gtm@crustdata.co) to discuss higher limits if needed for your use case.
</Note>




## OpenAPI

````yaml /openapi-specs/2025-11-01/agent_search.yaml post /agent/searches
openapi: 3.0.3
info:
  title: Agent Search API
  version: '2025-11-01'
  description: >
    The Agent Search API (beta) takes a question in plain words and returns the
    matching

    people, companies or jobs, each with the reasons it matches. You send the
    question to `POST /agent/searches`. An agent searches the

    Crustdata datasets in the background, picks the columns of the answer, and
    checks each

    row against the requirements it read in your question.


    A search runs for tens of seconds to a few minutes. Read it with

    `GET /agent/searches/{search_id}` until `state` is `succeeded`, `failed` or
    `cancelled`,

    or stream its events with `Accept: text/event-stream`.


    All requests require a valid API key passed via the `Authorization` header
    as a Bearer

    token and an `x-api-version` header set to `2025-11-01`.
servers:
  - url: https://api.crustdata.com
    description: Production API server
security:
  - bearerAuth: []
tags:
  - name: Agent Search APIs
    description: Start, read, stream and cancel agent searches.
paths:
  /agent/searches:
    post:
      tags:
        - Agent Search APIs
      summary: Start an agent search
      description: >
        Start a search from a question in plain words. The body has one key,
        `query`.


        The `Accept` request header picks the response:


        - `application/json` (the default; `*/*` or no `Accept` header gives the
        same)
          returns the `search_id` and the state of the search at that moment.
        - `text/event-stream` returns the event stream of the search, from its
        first
          event to `search.finished`. The `X-Search-Id` response header carries the
          `search_id`, so you can resume on `GET /agent/searches/{search_id}/events` if
          the stream breaks.

        Error responses are JSON whatever `Accept` says.


        A start returns `202` with the new `search_id` and its state, which is
        `running`.

        If you send the same `Idempotency-Key` with the same query again, the
        response is

        `200` with the current state of the first search, and that search can
        already be

        over. Always read `state` from the body, not from the status code. Treat
        every

        state that is not `succeeded`, `failed` or `cancelled` as still in
        progress,

        because a later release adds `queued`.


        Only `POST` is allowed on this path. A `GET` returns `405` with

        `{"detail": "Method \"GET\" not allowed."}`, and it counts toward the
        start limit.


        The start costs nothing and returns `X-Credits-Used: 0`. The first read
        after the

        search ends charges it. Before a search begins, the API checks that your
        balance

        covers 8 credits for each search of your account that has no charge yet,
        this one

        included. The check takes no credits, and 8 credits is not the price of
        a search:

        a search usually costs much less.


        <Note>
            Default `rate-limit` is 6 starts per minute for each account. A request that gets an error counts toward it too. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (the seconds until the limit resets). Send an email to [gtm@crustdata.co](mailto:gtm@crustdata.co) to discuss higher limits if needed for your use case.
        </Note>
      operationId: startAgentSearch
      parameters:
        - $ref: '#/components/parameters/ApiVersion'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        description: The question, in plain words.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentQueryRequest'
            examples:
              people_list:
                summary: A list of people
                description: A people list with one requirement.
                value:
                  query: The founders of Crustdata
              company_list:
                summary: A list of companies
                description: A company list with a location and a size range.
                value:
                  query: Fintech companies in Berlin with 50 to 200 employees
      responses:
        '200':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
            X-Search-Id:
              $ref: '#/components/headers/XSearchId'
          description: >-
            A repeat of an `Idempotency-Key` with the same query: the body has
            the first search's `search_id` and its current state, which can
            already be `succeeded`, `failed` or `cancelled`. A streaming start
            also returns `200`, with the event stream as the body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentSearchStarted'
              examples:
                repeat_after_end:
                  summary: >-
                    The same Idempotency-Key and query, sent after the first
                    search ended
                  value:
                    search_id: 1318-e4b7cbfc1298ebcbcb05239765ea93bc
                    state: cancelled
                repeat:
                  summary: The same Idempotency-Key and the same query, sent again
                  value:
                    search_id: 1318-07fcae93f67c961a96c8758766b2fea1
                    state: running
            text/event-stream:
              schema:
                type: string
                description: >-
                  Server-sent events. Each frame has `id` (the event's `seq`),
                  `event` (its `type`) and `data` (the event as JSON). See
                  `AgentSearchEvent`. A line that starts with `:` is a comment.
              examples:
                stream:
                  summary: The first 2 frames of a stream
                  value: >
                    id: 1

                    event: search.created

                    data: {"seq":1,"type":"search.created","message":"Search
                    created.","version":1,"search_id":"1318-c379345672f34397a53fafff6dcfaa83","created_at":"2026-11-13T11:32:55.577Z"}


                    id: 2

                    event: run.started

                    data:
                    {"seq":2,"data":{"run":1,"max_runs":2},"type":"run.started","message":"Run
                    1 of 2
                    started.","version":1,"search_id":"1318-c379345672f34397a53fafff6dcfaa83","created_at":"2026-11-13T11:32:55.583Z"}
        '202':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          description: A new search started. Its `state` is `running`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentSearchStarted'
              examples:
                started:
                  summary: A new search
                  value:
                    search_id: 1318-07fcae93f67c961a96c8758766b2fea1
                    state: running
        '400':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          description: >-
            The body or the `Idempotency-Key` header is not valid. Nothing
            started.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                other_key:
                  summary: The body has a key other than query
                  value:
                    error:
                      type: invalid_request
                      message: >-
                        The body must be a JSON object with `query` as its only
                        key.
                      metadata: []
                bad_query:
                  summary: The query is empty, too long, or holds a NUL character
                  value:
                    error:
                      type: invalid_request
                      message: >-
                        `query` must have 1 to 10,000 characters and no NUL
                        character.
                      metadata: []
                bad_idempotency_key:
                  summary: >-
                    The Idempotency-Key is not 1 to 255 printable ASCII
                    characters
                  value:
                    error:
                      type: invalid_request
                      message: >-
                        `Idempotency-Key` must have 1 to 255 printable ASCII
                        characters.
                      metadata: []
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          description: >-
            The balance check failed: your credits, or the monthly limit of this
            API key, do not cover 8 credits for each search of your account that
            has no charge yet, this one included. The check takes no credits and
            is not a price. These messages come from the api code and were not
            reproduced live.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                account_credits:
                  summary: The account's credits do not cover a new search
                  value:
                    error:
                      type: insufficient_credits
                      message: >-
                        Your credits do not cover a new search. Each search
                        needs 8 credits until it is charged.
                      metadata: []
                key_limit:
                  summary: The API key's monthly credit limit is used up
                  value:
                    error:
                      type: insufficient_credits
                      message: >-
                        This API key's monthly credit limit has been exhausted.
                        It resets on Dec 01, 2026 (UTC). A workspace admin can
                        raise or disable the limit in the dashboard.
                      metadata: []
        '403':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          description: >-
            Your account has no access to one of the endpoints that Agent Search
            uses. The message names the missing ones. This message comes from
            the api code and was not reproduced live.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missing_endpoint:
                  summary: The account cannot call /job/search
                  value:
                    error:
                      type: permission_error
                      message: >-
                        Agent Search needs access to /agent/query,
                        /person/search, /company/search and /job/search.
                        Missing: /job/search.
                      metadata: []
        '409':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
          description: >-
            The `Idempotency-Key` belongs to an earlier search with a different
            query.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                other_query:
                  summary: The same Idempotency-Key with a different query
                  value:
                    error:
                      type: invalid_request
                      message: >-
                        This Idempotency-Key belongs to a search with a
                        different query.
                      metadata: []
        '429':
          description: >-
            More than 6 requests to `POST /agent/searches` in one minute, or, on
            a streaming start, too many open event streams. The rate-limit `429`
            carries no `X-Credits-Used` and no `Retry-After` header: wait for
            the seconds in `X-RateLimit-Reset`. The stream `429` carries
            `Retry-After: 5`.
          headers:
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rate_limit:
                  summary: More than 6 starts in one minute
                  value:
                    error:
                      type: rate_limit_error
                      message: >-
                        Rate limit exceeded for this endpoint. Please write to
                        gtm@crustdata.co.
                      metadata: []
                too_many_streams:
                  summary: Too many event streams are open
                  value:
                    error:
                      type: rate_limit_error
                      message: >-
                        Too many event streams are open. Read the events list
                        instead, or try again in 5 seconds.
                      metadata:
                        - retryable: true
                          retry_after_seconds: 5
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          headers:
            X-Credits-Used:
              $ref: '#/components/headers/XCreditsUsed'
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
          description: >-
            Agent Search is at capacity or did not answer. Nothing started. Try
            again after `Retry-After` seconds. These messages come from the api
            code and were not reproduced live.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                at_capacity:
                  summary: Every search slot is busy
                  value:
                    error:
                      type: service_unavailable
                      message: Agent Search is at capacity. Try again in 60 seconds.
                      metadata:
                        - retryable: true
                          retry_after_seconds: 60
                not_answered:
                  summary: Agent Search did not answer the start
                  value:
                    error:
                      type: service_unavailable
                      message: Agent Search did not answer. Try again in 60 seconds.
                      metadata:
                        - retryable: true
                          retry_after_seconds: 60
components:
  parameters:
    ApiVersion:
      name: x-api-version
      in: header
      required: true
      schema:
        type: string
        enum:
          - '2025-11-01'
        default: '2025-11-01'
        example: '2025-11-01'
      description: API version to use. This endpoint currently requires `2025-11-01`.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >-
        1 to 255 printable ASCII characters. A start that repeats a key of your
        account with the same query returns the first search instead of starting
        a new one. The same key with a different query returns `409`.
      schema:
        type: string
        minLength: 1
        maxLength: 255
        example: crm-sync-2026-11-13-001
  schemas:
    AgentQueryRequest:
      type: object
      description: The start body. A key other than `query` returns `400`.
      additionalProperties: false
      required:
        - query
      properties:
        query:
          type: string
          minLength: 1
          maxLength: 10000
          description: >-
            The question in plain words, with no NUL character. Name the entity
            you want (people, companies or jobs) and the requirements.
          example: The founders of Crustdata
    AgentSearchStarted:
      type: object
      description: A search id and its state at the moment of the response.
      required:
        - search_id
        - state
      properties:
        search_id:
          type: string
          description: The id of the search. Use it on every other route.
          example: 1318-07fcae93f67c961a96c8758766b2fea1
        state:
          $ref: '#/components/schemas/AgentSearchState'
    ErrorResponse:
      type: object
      description: >-
        The body of almost every error response. A `405` and a `404` on an
        unknown path have another body.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - message
            - metadata
          properties:
            type:
              type: string
              example: invalid_request
            message:
              type: string
            metadata:
              type: array
              description: >-
                Empty, or `[{"retryable": true, "retry_after_seconds": N}]` on a
                error that can clear if you try again later.
              items:
                type: object
                properties:
                  retryable:
                    type: boolean
                  retry_after_seconds:
                    type: integer
    AgentSearchState:
      type: string
      description: >-
        The state of a search. `succeeded`, `failed` and `cancelled` are end
        states. Every search in progress is `running` today. `queued` is not
        returned today; a later release adds it for a search that waits in a
        queue. Treat every state that is not an end state as in progress. This
        enum is closed.
      enum:
        - queued
        - running
        - succeeded
        - failed
        - cancelled
      example: running
  headers:
    XCreditsUsed:
      description: >-
        Exact credits this request deducted, as a decimal string (for example
        `0.9125`). On these routes, only the first read after a search ends
        carries a value above `0`. Responses generated before a request reaches
        the API, such as rate-limit `429`s, do not carry it.
      schema:
        type: string
        example: '0.9125'
    XSearchId:
      description: >-
        The `search_id` of a streaming start, so a client can resume on the
        events route.
      schema:
        type: string
        example: 1318-c379345672f34397a53fafff6dcfaa83
    RetryAfter:
      description: Seconds to wait before you try again.
      schema:
        type: string
        example: '5'
  responses:
    Unauthorized:
      description: The API key is missing or not valid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalid_api_key:
              summary: No API key, or an API key that is not valid
              value:
                error:
                  type: unauthorized
                  message: Invalid API key in request.
                  metadata: []
    InternalError:
      description: >-
        The search could not be recorded. This message comes from the api code
        and was not reproduced live.
      headers:
        X-Credits-Used:
          $ref: '#/components/headers/XCreditsUsed'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            internal_error:
              summary: The search could not be recorded
              value:
                error:
                  type: internal_error
                  message: An unexpected error occurred.
                  metadata: []
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key passed as a Bearer token in the `Authorization` header.

````

## Related topics

- [Agent Search API](https://docs.crustdata.com/agent-search-docs/overview.md)
- [API Introduction](https://docs.crustdata.com/openapi-specs/2025-11-01/introduction.md)
- [Search](https://docs.crustdata.com/api-reference/web-apis/web-search.md)
- [Web Search API](https://docs.crustdata.com/web-docs/search/introduction.md)
- [Web Search reference](https://docs.crustdata.com/web-docs/search/reference.md)
