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

# Search lifecycle

> How an asynchronous search moves from queued to finished, and what each status and stop reason means.

Finding and checking candidates takes time, and how long depends on the request. So a search is asynchronous (The API accepts your request and answers immediately, before the work is done. You check back for the outcome.): starting one returns immediately, and you read its progress and results while it runs.

## Watch a search run

Start the simulated search below. The left side is what your code sends. The right side is what the search reports back. Try cancelling part-way through.

> [!NOTE]
>
> This is a simulation. The companies, counts and credits are illustrative, and the real pace depends on the request.

Sequence shown by the simulation: `POST /companies/search` returns `202` with `status: queued`. Each poll of `GET .../status` returns `running` with rising `progress` counts and `credits_charged`. Each `GET .../results?after=<next_cursor>` returns only the results found since the previous call. The search ends as `completed` with `stop_reason: target_reached` when `accepted` equals `limit`. If `POST .../cancel` is sent first, the response has `cancel_requested: true`, work already in flight still lands and is charged, and the search then ends as `cancelled`.

## Statuses

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
stateDiagram-v2
    direction LR
    [*] --> queued: POST search (202)
    queued --> running
    queued --> completed: nothing to search
    queued --> failed
    running --> completed
    running --> failed
    queued --> cancelled: cancel
    running --> cancelled: cancel
    completed --> [*]
    failed --> [*]
    cancelled --> [*]
```

| `status` | Meaning | Final |
| - | - | - |
| `queued` | Accepted. The search is being set up: the query is interpreted and the first candidates are fetched. | No |
| `running` | Candidates are being checked. Results can already be read. | No |
| `completed` | The run finished. `stop_reason` says why it stopped. | Yes |
| `failed` | The run could not finish. Results found before the failure stay readable. | Yes |
| `cancelled` | You cancelled it. Results found before cancellation stay readable. | Yes |

The last three are **terminal statuses**: once a search reaches one, it does not change again, and `credits_charged` is final.

Three moments are worth telling apart:

* **Accepted** is the `202` response. The search exists and has an `id`.
* **Working** is `queued` followed by `running`. Data providers are being called and credits are being spent.
* **Finished** is a terminal status. No more work will start.

## Stop reasons

When a search finishes, `stop_reason` tells you why. It is `null` while the search is in progress.

| `stop_reason` | Status | What happened | What to do |
| - | - | - | - |
| `target_reached` | `completed` | Found `limit` results. | Nothing. To get more, start another search that excludes what you already have: `exclude_domains` for companies, `exclude_linkedin_urls` for people. |
| `sources_exhausted` | `completed` | Ran out of candidates before reaching `limit`. | Loosen a requirement, or accept that few records match. |
| `usage_limit_reached` | `completed` | Reached `max_credits`, or the workspace ran out of credits. | Raise `max_credits` or add credits, then search again. |
| `retrieval_limit_reached` | `completed` | Checked the most candidates one search allows without reaching `limit`. | Tighten the filters so fewer candidates need checking. |
| `query_needs_clarification` | `completed` | The `query` could not be searched as written: a requirement cannot be checked, or there was nothing to search on. Nothing was retrieved or charged. | Read `interpretation.unsupported_criteria` and `interpretation.errors`, then reword. |
| `provider_failed` | `failed` | Every data source failed. | Retry later. Results found before the failure are kept. |
| `cancelled` | `cancelled` | You called cancel. | Nothing. |

A `failed` search can also have a `stop_reason` of `null`, for example when it ran past its time limit. Read `error` for the explanation.

> [!WARNING]
>
> `completed` means this run finished. It does not mean every matching company in the world was found. A search with `stop_reason: target_reached` stopped because it found the number you asked for, not because there was nothing left.

## Progress

While a search runs, `progress` counts what has happened to the candidates so far:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "status": "running",
  "progress": {
    "candidates": 40,
    "accepted": 6,
    "rejected": 31,
    "unknown": 3,
    "not_returned": 0,
    "contacts_found": 0
  },
  "credits_charged": 2.94
}
```

**candidates** `integer`

Candidates checked so far.

**accepted** `integer`

Matched every requirement and were added to the results.

**rejected** `integer`

Failed at least one requirement.

**unknown** `integer`

Could not be confirmed on every requirement. Not returned. See [Evidence and verdicts](https://docs.data.avina.io/concepts/evidence.md).

**not\_returned** `integer`

Matched every requirement, but `limit` or a spend limit had already been reached.

**contacts\_found** `integer`

Contacts found, for company searches with personas and people searches with enrichment.

The total number of matches is not known in advance, so `candidates` cannot be turned into a percentage. To show progress, use `accepted` out of `limit`.

## Reading results while it runs

Results are only ever added, never changed or removed, and they appear as soon as they are accepted. You do not need to wait for the search to finish before reading them. Most integrations use this loop:

**1. Poll status**

Every 2 seconds, slowing to every 10. Stop when `status` is terminal.

**2. Read new results**

Call results with `after` set to the last `next_cursor` you saw. You get only what was found since then.

**3. Read the rest after the search ends**

Once the status is terminal, keep reading results until `has_more` is `false`.

[Poll and paginate](https://docs.data.avina.io/guides/poll-and-paginate.md) has complete code for this loop.

## Cancelling

`POST .../cancel` stops a search from starting new work.

* Cancellation is not instant. Provider calls already in progress may finish, add results and be charged. The response is `202` with `cancel_requested: true`, and the search reaches `cancelled` once that work has finished.
* Cancelling a finished search returns it unchanged with `200`.
* What was already charged stays charged. What was found stays readable.

> [!NOTE]
>
> Closing your connection, timing out a request or stopping your polling loop does **not** cancel a search. The search keeps running and spending until it finishes or you cancel it.

## Failure and partial results

A failed search is not an empty search. If a search fails after finding 12 companies, those 12 are still in its results and `error` explains what went wrong:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "status": "failed",
  "stop_reason": "provider_failed",
  "error": "The search failed; results found before the failure are still readable.",
  "progress": { "candidates": 55, "accepted": 12, "rejected": 40, "unknown": 3, "not_returned": 0, "contacts_found": 0 }
}
```

When only some data sources fail, the search carries on with the rest and finishes normally.

## How long results last

A search and its results stay readable after it finishes. `expires_at` is the time after which they are no longer available, or `null` when no expiry is set. Store what you need in your own system rather than treating a search as permanent storage.

## Next steps

> [card] [Poll and paginate](https://docs.data.avina.io/guides/poll-and-paginate.md)
>
> Code for the polling loop and cursor pagination.

> [card] [Errors and retries](https://docs.data.avina.io/guides/errors-and-retries.md)
>
> Idempotency and safe retries.


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