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

# Text to Filters

> Convert natural language queries into structured filters

Convert a natural language query into structured filters that can be used with `/search/people` or `/search/companies` endpoints.

<Tip>
  For the most relevant results from a natural language query, use [Reasoning Search](/api-reference/reasoning-search): an AI agent probes the database, asks clarifying questions when needed, and builds the best possible search. Use Text to Filters when you only need the filters, or the [one-shot search](#one-shot-search) below for a single fast query-to-results call.
</Tip>

## Credit Cost

| Action            | Credits  |
| ----------------- | -------- |
| Filter generation | 1 credit |

***

## Request Body

<ParamField body="query" type="string" required>
  Natural language search query (minimum 3 characters).

  Examples:

  * `"développeurs Python à Paris"`
  * `"CEOs of fintech startups in London"`
  * `"50 data scientists with 5+ years experience"`
</ParamField>

<ParamField body="category" type="string" required>
  Category to generate filters for: `"people"` or `"company"`.
</ParamField>

<ParamField body="lookalike_use_case" type="string" default="sales">
  Use case for lookalike search. Only applies when the query contains a LinkedIn profile URL (e.g., `linkedin.com/in/...`), which is automatically detected.

  * `"sales"` — Generate filters similar from a sales/prospecting perspective
  * `"recruiter"` — Generate filters similar from a recruiting perspective
</ParamField>

<ParamField body="with_total" type="boolean" default="false">
  When `true`, runs a count on the generated filters and returns `total_results` — the number of matching records. The count is capped at 10,000.
</ParamField>

<ParamField body="previous_query" type="string">
  The query from a previous call, for **incremental refinement**. Provide it together with `previous_filters`: the model re-extracts the full filter set anchored on what was already detected, so the output stays stable when the user edits their query. Filters whose intent is unchanged are kept identical; filters the new query no longer mentions are dropped.
</ParamField>

<ParamField body="previous_filters" type="object">
  The `simple_filters` object returned by the previous call. Required alongside `previous_query` for incremental refinement.
</ParamField>

***

## Response

<ResponseField name="category" type="string">
  The category for the filters (`"people"` or `"company"`).
</ResponseField>

<ResponseField name="filters" type="object">
  Structured filters in FilterGroup format, directly usable with `/search/people` or `/search/companies`.

  <Expandable title="properties">
    <ResponseField name="op" type="string">
      Logical operator: `"and"` or `"or"`.
    </ResponseField>

    <ResponseField name="conditions" type="array">
      Array of filter conditions or nested FilterGroups.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="requested_count" type="integer | null">
  Count detected in the query if specified (e.g., `"50 devs"` → `50`), otherwise `null`.
</ResponseField>

<ResponseField name="simple_filters" type="object | null">
  The flat key/value filters detected by the model, before expansion into the nested FilterGroup. Pass this object back as `previous_filters` on the next call to enable incremental refinement.
</ResponseField>

<ResponseField name="total_results" type="integer | null">
  Number of matching records. Populated only when `with_total` is `true`. Capped at 10,000.
</ResponseField>

<ResponseField name="total_results_is_capped" type="boolean | null">
  `true` when `total_results` reached the 10,000 cap — the real count is higher (display it as "10,000+").
</ResponseField>

<ResponseField name="cached" type="boolean">
  `true` when the filters were served from cache (no LLM call was made for this request).
</ResponseField>

***

## Incremental refinement

When a user edits a query, pass the previous call's `query` and `simple_filters` back as `previous_query` and `previous_filters`. The model refines the existing filters instead of recomputing from scratch — keeping unchanged criteria stable and only applying the delta. Both parameters must be provided together.

<CodeGroup>
  ```json First call theme={null}
  {
    "query": "VP of Sales at SaaS companies",
    "category": "people",
    "with_total": true
  }
  ```

  ```json Refinement theme={null}
  {
    "query": "VP of Sales at SaaS companies in New York",
    "category": "people",
    "with_total": true,
    "previous_query": "VP of Sales at SaaS companies",
    "previous_filters": {
      "current_title": ["VP Sales", "Vice President of Sales"],
      "current_company_category": ["SaaS"]
    }
  }
  ```
</CodeGroup>

***

## One-shot search

`POST /search/llm` takes the same `query`, `category` and `lookalike_use_case` parameters but runs the search immediately and returns results in a single call — no separate `/search/people` request needed.

Two extra parameters:

<ParamField body="count" type="integer" default="25">
  Number of results to return. Maximum: 100.
</ParamField>

<ParamField body="enrich_live" type="boolean" default="true">
  Whether to enrich results with live data.

  * `true` — Returns fresh, up-to-date data (1.5 credits per result)
  * `false` — Returns cached data (0.75 credits per result)
</ParamField>

The response contains `query_interpretation` (how the AI read your query, including the `advanced_filters` it applied), `total`, `count` and `results` — profiles or companies in the same structure as `/search/people` / `/search/companies`.

```bash theme={null}
curl -X POST https://api.dataforb2b.ai/search/llm \
  -H "api_key: YOUR_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "développeurs Python à Paris",
    "category": "people",
    "count": 10
  }'
```

<Info>
  Pagination is not available on this endpoint. To paginate, reuse `query_interpretation.advanced_filters` with `/search/people` or `/search/companies` and set an `offset`. For multi-turn refinement and clarifying questions, use [Reasoning Search](/api-reference/reasoning-search) instead.
</Info>

***

## Examples

<AccordionGroup>
  <Accordion title="People filters">
    ```json theme={null}
    {
      "query": "développeurs Python à Paris",
      "category": "people"
    }
    ```
  </Accordion>

  <Accordion title="Lookalike filters">
    ```json theme={null}
    {
      "query": "lookalike https://www.linkedin.com/in/thomas-kurian-469b6219/",
      "category": "people",
      "lookalike_use_case": "recruiter"
    }
    ```
  </Accordion>

  <Accordion title="Company filters with count">
    ```json theme={null}
    {
      "query": "50 fintech startups in London",
      "category": "company"
    }
    ```
  </Accordion>

  <Accordion title="With result count">
    ```json theme={null}
    {
      "query": "data scientists at Google",
      "category": "people",
      "with_total": true
    }
    ```
  </Accordion>
</AccordionGroup>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.dataforb2b.ai/search/llm/filters \
    -H "api_key: YOUR_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "développeurs Python à Paris",
      "category": "people"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.dataforb2b.ai/search/llm/filters",
      headers={
          "api_key": "YOUR_api_key",
          "Content-Type": "application/json"
      },
      json={
          "query": "développeurs Python à Paris",
          "category": "people"
      }
  )

  data = response.json()
  # Use filters with /search/people
  search_response = requests.post(
      "https://api.dataforb2b.ai/search/people",
      headers={
          "api_key": "YOUR_api_key",
          "Content-Type": "application/json"
      },
      json={
          "filters": data["filters"],
          "count": data["requested_count"] or 25
      }
  )
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.dataforb2b.ai/search/llm/filters', {
    method: 'POST',
    headers: {
      'api_key': 'YOUR_api_key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      query: 'développeurs Python à Paris',
      category: 'people'
    })
  });

  const data = await response.json();
  // Use filters with /search/people
  const searchResponse = await fetch('https://api.dataforb2b.ai/search/people', {
    method: 'POST',
    headers: {
      'api_key': 'YOUR_api_key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      filters: data.filters,
      count: data.requested_count || 25
    })
  });
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "category": "people",
    "filters": {
      "op": "and",
      "conditions": [
        {
          "op": "or",
          "conditions": [
            {"column": "skills", "type": "like", "value": "Python"},
            {"column": "headline", "type": "like", "value": "Python"},
            {"column": "current_title", "type": "like", "value": "Python Developer"}
          ]
        },
        {
          "op": "or",
          "conditions": [
            {"column": "location", "type": "like", "value": "Paris"},
            {"column": "country", "type": "eq", "value": "FR"}
          ]
        }
      ]
    },
    "requested_count": null,
    "simple_filters": {
      "skill": ["Python"],
      "profile_location": ["Paris"]
    },
    "total_results": null,
    "total_results_is_capped": null,
    "cached": false
  }
  ```
</ResponseExample>
