# Search the web

`POST https://api.endless.app/search`

Source: https://endless.app/docs/api/search/web

Ranked web results for a query through Exa — title, URL, a short snippet and the captioned photos found on each page. Charged as one flat call; a filtered search that returns nothing is retried once without its filters at no extra charge. A search scoped to a social platform is refused (400) with the social operations that read it.

## Authentication

- `bearerAuth`: HTTP bearer (JWT)

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Endless-Api-Version` | header | string | no | Pin the caller to an API major (`1`). Omit to accept the current version. A major this deployment cannot serve is refused with 400 `unsupported_api_version`. |

## Request body

Required.

Content type: `application/json` — object

- `organization_id` (string, required)
- `tool_call_id` (string | null): The tool call behind this search, for the ledger. Every call that runs is billed, a repeated id too.
- `conversation_id` (string (uuid) | null): The chat this call belongs to, for cost attribution.
- `message_id` (string (uuid) | null): The chat turn this call belongs to, for cost attribution.
- `query` (string, required): The search query, phrased as you'd type it into a search engine. For pictures, search the subject together with its qualifier in the language the sources use; appending 'photos' only ranks stock libraries.
- `num_results` (integer): How many results to return (max 10).
- `category` ("publication" | "news" | "personal site" | "financial report"): Optional focus area — narrows results to one content type, so only set it when the query targets that exact type: 'news' for recent articles, 'publication' for academic work, 'financial report' for filings, 'personal site' for blogs. Leave it UNSET for how-tos, documentation, support articles, or general questions — a category will miss those and return nothing.
- `time_range` ("day" | "week" | "month" | "year"): Only return pages published within this window back from today.
- `include_domains` (array<string>): Restrict results to these domains (e.g. ['nasa.gov']).
- `exclude_domains` (array<string>): Drop results from these domains.

## Responses

### 200 OK

Content type: `application/json` — object

- `summary` (string, required)
- `results` (array<object>, required)
  - `title` (string, required)
  - `url` (string, required)
  - `published_date` (string)
  - `author` (string)
  - `images` (array<object>)
    - `url` (string, required)
    - `caption` (string)
  - `snippet` (string)
- `credits_charged` (integer, required)

### Errors

- `400` (Error): The request is malformed, or failed input validation. `message` names the field and the rule it broke; `data.issues` carries them individually.
- `401` (Error): No bearer token, or a token that failed verification.
- `402` (Error): The workspace has no credits left, or the caller is over a spending limit.
- `403` (Error): Authenticated, but the caller's role in this workspace does not carry the permission this operation needs.
- `409` (Error): The request is well-formed, but the resource's current state forbids it — a taken name, a last admin, a limit already reached.
- `429` (Error): The caller has spent its rate-limit window. `Retry-After` says when to come back.
- `500` (Error): The request failed on our side. Safe to retry an idempotent operation with backoff.
