# Deep Search (/search/deep-search)

`POST https://api.keirolabs.cloud/api/v2/deep`

Cost: 5 credits · Latency: 15-50 s

Our flagship web API — 96% SimpleQA score, the most accurate answer layer we ship. Give it a question and it searches the web, crawls every top result in full, and orders them by how likely each page actually contains the answer, so results[0] is the page to read first rather than whatever the SERP put there. Every result carries an excerpt (the passage most likely to hold the answer) alongside the full page text, so one call returns the whole reading list already extracted. Set content: false to leave the page text out and get excerpts only.

**When to use it.** Use Deep when you need the answer rather than a list of links: research questions, due diligence, fact-checking, or any pipeline where a person or a model will read the top page. Because it returns the full text of every result, one Deep call often replaces a search plus N separate scrape calls — and it is frequently cheaper than that combination. On an agent path, call it twice: once with content: false to read every excerpt and likelihood cheaply, then again for the one or two pages you actually need. The second call repeats the query, so it is served from cache.

**When not to.** Don't use Deep on interactive paths. At 15-50 seconds it is wrong for autocomplete, live chat, or anything a user is waiting on — use Search or Lite there. Don't use it for a plain SERP (Search, 0.5 credits) or when you already know which URL to read (Extract, 2 credits). Set your client timeout to at least 120 seconds. Note there is no page-count setting: how many pages get crawled and read is chosen for you, so the depth is consistent rather than tuned per call.

## Request

**cURL**

```bash
curl -X POST https://api.keirolabs.cloud/api/v2/deep \
  -H "Authorization: Bearer $KEIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "how do EU AI Act obligations differ for general purpose models?"}'
```

**Python**

```python
import os, requests

res = requests.post(
    "https://api.keirolabs.cloud/api/v2/deep",
    headers={"Authorization": f"Bearer {os.environ['KEIRO_API_KEY']}"},
    json={
        "query": "how do EU AI Act obligations differ for general purpose models?"
    },
)
print(res.json())
```

**Node.js**

```js
const res = await fetch("https://api.keirolabs.cloud/api/v2/deep", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.KEIRO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "query": "how do EU AI Act obligations differ for general purpose models?"
  }),
});
console.log(await res.json());
```

## Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `query` | string | yes | The question to answer (1-500 chars). Phrase it as a question — accuracy is tuned for question-shaped queries. |
| `content` | boolean | no | Default true. Set false to leave the full page text out of the response and get only excerpt plus the likelihood scores — roughly 150 tokens per result instead of about 25,000. The one to use on an agent path, where you rarely need more than the passage. |

## Response

```json title="200 OK"
{
  "query": "who invented the telephone",
  "results": [
    {
      "position": 1,
      "serp_rank": 9,
      "title": "Alexander Graham Bell - Wikipedia",
      "url": "https://en.wikipedia.org/wiki/Alexander_Graham_Bell",
      "snippet": "Alexander Graham Bell was a Scottish-born inventor, scientist and engineer\u2026",
      "excerpt": "Bell is credited with patenting the first practical telephone in 1876, following months of work on the harmonic telegraph. The patent, US 174,465, was granted on March 7, 1876\u2026",
      "content": "Alexander Graham Bell (March 3, 1847 \u2013 August 2, 1922) was a Scottish-born inventor, scientist and engineer who is credited with patenting the first practical telephone\u2026",
      "content_status": "fetched",
      "answer_likelihood": 0.97
    },
    {
      "position": 2,
      "serp_rank": 3,
      "title": "Who really invented the telephone? - BBC Science Focus",
      "url": "https://www.sciencefocus.com/science/who-really-invented-the-telephone",
      "snippet": "The question of who invented the telephone has been argued over for more than a century\u2026",
      "excerpt": "Antonio Meucci, Alexander Graham Bell and Elisha Gray all played a part, and the US House of Representatives passed a resolution in 2002 recognising Meucci\u2026",
      "content": "Antonio Meucci, Alexander Graham Bell and Elisha Gray all played a part\u2026",
      "content_status": "fetched",
      "answer_likelihood": 0.93
    }
  ],
  "meta": {
    "latency_ms": 45204,
    "fetched": 7,
    "ranked": true,
    "retry": false
  },
  "count": 2,
  "creditsCharged": 5,
  "credits_used": 5,
  "creditsRemaining": 22495
}
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `results` | array | no | Pages ordered by answer likelihood, most likely first. |
| `results[].position` | integer | no | Position in the returned reading list — 1 is the page most likely to answer. Always sequential from 1 with no gaps. |
| `results[].title` | string | no | Page title. Can be empty on a DOI-resolver hop, where url is still valid. |
| `results[].url` | string | no | Full URL of the page. |
| `results[].snippet` | string | no | Search-result excerpt. May be empty. |
| `results[].excerpt` | string | no | Around 700 characters pulled from the region of the page most likely to hold the answer. Read this before content: it answers most questions for a fraction of the tokens. Present whether or not content was requested. |
| `results[].content` | string | no | Full extracted page text with images and boilerplate stripped. Uncapped — up to roughly 100,000 characters per page — so size responses for it. Empty in two cases: the page was blocked (content_status is "blocked"), or the request set content: false. Empty does not by itself mean the page failed. |
| `results[].content_status` | string | no | "fetched" or "blocked". A blocked page still appears because its position is still the right one to read; only its text is missing. Stays "fetched" when content: false was used, since the page was read fine and the text was simply not returned. |
| `results[].answer_likelihood` | number | no | 0-1 score for how likely this page contains the answer. null on DOI hops, which sort last. |
| `results[].serp_rank` | integer | no | Where the page sat in the original search results, 0-based, before this endpoint ordered them. Compare it against position to see the reordering at work: position 1 with serp_rank 9 means the ninth search result was promoted because its content actually answers the question. |
| `meta.latency_ms` | integer | no | Server-side time for the call. |
| `meta.fetched` | integer | no | How many pages were crawled successfully. |
| `meta.ranked` | boolean | no | false when the answer-ordering step was unavailable and raw SERP order was returned instead. Check this before trusting the order. |
| `meta.retry` | boolean | no | true when the ordering pass was retried. |
| `meta.cache` | boolean | no | Present and true only when the response was served from cache — a repeat of the same query and content setting within 15 minutes. Cached responses still cost 5 credits. |
| `count` | integer | no | Number of results returned. |
| `creditsCharged` | number | no | Always 5. Charged per call rather than per result, and identical on every plan including free. |
| `creditsRemaining` | number | no | Your remaining credit balance. |

## Errors

| Status | Code | Cause | Fix |
| --- | --- | --- | --- |
| `400` | `VALIDATION_ERROR` | Missing query, query longer than 500 characters, or a content value that is not a boolean | Send a non-empty query and, if you are setting it, a true or false content flag. |
| `401` | `UNAUTHORIZED` | Missing or invalid API key | Check your Authorization: Bearer header. |
| `402` | `INSUFFICIENT_CREDITS` | Fewer than 5 credits available | Top up or upgrade at /dashboard/billing. Nothing was charged. |
| `502` | `SEARCH_UNAVAILABLE` | A page crawl timed out, or the backend was unreachable | Nothing was charged. Retry with exponential backoff. |
| `502` | `SEARCH_UNAVAILABLE` | The answer backend returned an error | Nothing was charged. Retry; if it persists, try again later. |
| `503` | `SERVICE_BUSY` | Deep Search is at capacity right now | Nothing was charged. Wait for Retry-After seconds, then retry. Concurrency is capped, so back off rather than retrying in parallel. |

## Rate limits

| Tier | Limit |
| --- | --- |
| Free | 2/min |
| Starter | 4/min |
| Pro | 6/min |
| Startup | 10/min |
| Enterprise | 20/min |

Over the limit you get a `429`. See [Rate limits](/rate-limits) for headers and backoff.