---
title: Overview
description: Cloudflare AI Search is a managed search service. Index your content and query it with natural language from a Workers binding, REST API, or MCP server.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Overview

The search primitive for your applications and agents.

 Available on all plans 

AI Search lets you add search to any application or agent without having to build an entire retrieval infrastructure. Create an instance, give it your data, and search it with natural language.

[ Upload or connect ](https://developers.cloudflare.com/ai-search/configuration/data-source/) 

AI Search 

[ Storage ](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) [ AI models ](https://developers.cloudflare.com/ai-search/configuration/models/) [ Index ](https://developers.cloudflare.com/ai-search/configuration/indexing/) 

[ Search ](https://developers.cloudflare.com/ai-search/concepts/search-modes/) 

You can use AI Search for:

* Documentation and knowledge base search
* AI agents that perform research over your internal knowledge
* Let each tenant or agent upload and search their own files

[ Get started ](https://developers.cloudflare.com/ai-search/get-started/)[ Watch AI Search demo ](https://www.youtube.com/watch?v=JUFdbkiDN2U)

---

## Features

###  Automated indexing 

Automatically and continuously index your data source, keeping your content fresh without manual reprocessing.

[ View indexing ](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) 

###  Metadata filtering 

Define custom metadata fields and filter search results by category, version, language, or any attribute you define.

[ Add filters ](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) 

###  Hybrid search 

Combine semantic and keyword matching in the same query for more accurate results.

[ Configure hybrid search ](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) 

###  MCP and UI snippets 

Every instance includes a built-in MCP endpoint for AI agents and embeddable search components for your website.

[ Connect agents ](https://developers.cloudflare.com/ai-search/api/search/mcp/) 

---

## Related products

**[Workers AI](https://developers.cloudflare.com/workers-ai/)** 

Run machine learning models, powered by serverless GPUs, on Cloudflare's global network.

**[AI Gateway](https://developers.cloudflare.com/ai-gateway/)** 

Observe and control your AI applications with caching, rate limiting, request retries, model fallback, and more.

**[Vectorize](https://developers.cloudflare.com/vectorize/)** 

Build full-stack AI applications with Vectorize, Cloudflare's vector database.

**[Workers](https://developers.cloudflare.com/workers/)** 

Build serverless applications and deploy instantly across the globe for exceptional performance, reliability, and scale.

**[R2](https://developers.cloudflare.com/r2/)** 

Store large amounts of unstructured data without the costly egress bandwidth fees associated with typical cloud storage services.

---

## More resources

[Get started](https://developers.cloudflare.com/ai-search/get-started/) 

Create your first AI Search instance and run your first query.

[Developer Discord](https://discord.cloudflare.com) 

Connect with the Workers community on Discord to ask questions, share what you are building, and discuss the platform with other developers.

[@CloudflareDev](https://x.com/cloudflaredev) 

Follow @CloudflareDev on Twitter to learn about product announcements, and what is new in Cloudflare Workers.

```json
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/ai-search/#page","headline":"Cloudflare AI Search · Cloudflare AI Search docs","description":"Cloudflare AI Search is a managed search service. Index your content and query it with natural language from a Workers binding, REST API, or MCP server.","url":"https://developers.cloudflare.com/ai-search/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-06","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"},"keywords":["AI"]}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}}]}
```

---

---
title: Get started
description: Create fully-managed, retrieval-augmented generation pipelines with Cloudflare AI Search.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Get started

AI Search is a managed search service. Connect a website, an R2 bucket, or upload your own documents, and AI Search indexes your content for natural language queries.

## Choose your setup method

[ Workers API ](https://developers.cloudflare.com/ai-search/get-started/workers/) Create, populate, and query an AI Search instance from a Cloudflare Worker. 

[ CLI ](https://developers.cloudflare.com/ai-search/get-started/wrangler/) Create and manage AI Search instances from the command line. 

[ Dashboard ](https://developers.cloudflare.com/ai-search/get-started/dashboard/) Create and configure AI Search using the Cloudflare dashboard. 

[ Python SDK ](https://developers.cloudflare.com/ai-search/get-started/python/) Create, populate, and query an AI Search instance from Python. 

[ REST API ](https://developers.cloudflare.com/ai-search/get-started/api/) Create AI Search instances programmatically using the REST API.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/get-started/#page","headline":"Get started with AI Search · Cloudflare AI Search docs","description":"Create fully-managed, retrieval-augmented generation pipelines with Cloudflare AI Search.","url":"https://developers.cloudflare.com/ai-search/get-started/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/get-started/","name":"Get started"}}]}
```

---

---
title: REST API
description: Create AI Search instances programmatically using the REST API.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# REST API

This guide walks you through creating an AI Search instance using the REST API.

## 1\. Create an API token

You need an API token with **AI Search:Edit** and **AI Search:Run** permissions.

1. In the Cloudflare dashboard, go to **My Profile** \> **API Tokens**.  
[ Go to **API Tokens** ](https://dash.cloudflare.com/profile/api-tokens)
2. Select **Create Token**.
3. Select **Create Custom Token**.
4. Enter a **Token name**, for example `AI Search Manager`.
5. Under **Permissions**, add two permissions:

  * **Account** \> **AI Search:Edit**
  * **Account** \> **AI Search:Run**
6. Select **Continue to summary**, then select **Create Token**.
7. Copy and save the token value. This is your `API_TOKEN`.

## 2\. Create an AI Search instance

Use the [Create instance API](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/create/) to create an instance. Replace `<ACCOUNT_ID>` with your [account ID](https://developers.cloudflare.com/fundamentals/account/find-account-and-zone-ids/).

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "id": "my-instance"
  }'
```

### Connect a data source (optional)

You can create an instance that is connected to a website or R2 bucket as a data source. AI Search indexes the content automatically.

**Website:**

Automatically crawl and index a [website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) that you own.

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "id": "my-instance",
    "type": "web-crawler",
    "source": "example.com"
  }'
```

**R2 bucket:**

Index documents stored in an [R2 bucket](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/). Connecting an R2 bucket requires a [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/). If you have never created an R2-backed instance before, you need to pass the `token_id` field in the create request. Refer to the [service API token configuration](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) for setup instructions.

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "id": "my-instance",
    "type": "r2",
    "source": "<R2_BUCKET_NAME>",
    "token_id": "<SERVICE_TOKEN_ID>"
  }'
```

## 3\. Add content

If you did not create an instance that is connected to a data source, upload files using the [Items API](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/upload/). You can skip this step if you connected a website or R2 bucket.

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/my-instance/items" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -F "file=@/path/to/your/file.pdf"
```

AI Search indexes uploaded files automatically.

## 4\. Check indexing status

Check if your content has finished indexing.

```bash
curl "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/my-instance/stats" \
  -H "Authorization: Bearer <API_TOKEN>"
```

## Try it out

Once indexing is complete, run your first query.

```bash
curl "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/my-instance/search" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "content": "How do I get started?",
        "role": "user"
      }
    ]
  }'
```

You can also test queries in the dashboard by going to your instance and selecting the **Playground** tab.

## Add to your application

[ Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Query AI Search directly from your Workers code. 

[ REST API ](https://developers.cloudflare.com/ai-search/api/search/rest-api/) Query AI Search using HTTP requests.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/get-started/api/#page","headline":"REST API · Cloudflare AI Search docs","description":"Create AI Search instances programmatically using the REST API.","url":"https://developers.cloudflare.com/ai-search/get-started/api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-03","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/get-started/","name":"Get started"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/get-started/api/","name":"REST API"}}]}
```

---

---
title: Dashboard
description: Create and configure AI Search using the Cloudflare dashboard.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Dashboard

This guide walks you through creating an AI Search instance using the Cloudflare dashboard.

## Create an AI Search instance

1. Go to **AI Search** in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select **Create Instance**.
3. Name your AI Search instance.
4. Optionally connect a [data source](https://developers.cloudflare.com/ai-search/configuration/data-source/) such as a website you own or an R2 bucket.
5. Review your configurations.
6. Select **Create**.

## Upload content

Once an instance has been created, you can upload files directly from the dashboard.

1. Go to **AI Search** in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your instance.
3. Select the **Items** tab.
4. Upload your files. AI Search indexes them automatically.

## Try it out

Once your content is indexed, you can run your first query.

1. Go to your AI Search instance.
2. Select the **Playground** tab.
3. Select **Chat** or **Search**.
4. Enter a query to test the response.

## Add to your application

There are multiple ways you can connect AI Search to your application:

[ Workers Binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Query AI Search directly from your Workers code. 

[ REST API ](https://developers.cloudflare.com/ai-search/api/search/rest-api/) Query AI Search using HTTP requests.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/get-started/dashboard/#page","headline":"Dashboard · Cloudflare AI Search docs","description":"Create and configure AI Search using the Cloudflare dashboard.","url":"https://developers.cloudflare.com/ai-search/get-started/dashboard/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/get-started/","name":"Get started"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/get-started/dashboard/","name":"Dashboard"}}]}
```

---

---
title: Python SDK
description: Create, populate, and query an AI Search instance from Python.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Python SDK

This guide walks you through creating an AI Search instance, uploading content, and querying it from a Python application using the [Cloudflare Python SDK ↗](https://github.com/cloudflare/cloudflare-python).

## Prerequisites

* [Python ↗](https://www.python.org/downloads/) 3.8 or later.
* Your [account ID](https://developers.cloudflare.com/fundamentals/account/find-account-and-zone-ids/).

This guide uses the `default` [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/), which exists automatically on every account. To group instances into your own namespace, create one with `client.aisearch.namespaces.create()`.

## 1\. Create an API token

You need an API token with **AI Search:Edit** and **AI Search:Run** permissions.

1. In the Cloudflare dashboard, go to **My Profile** \> **API Tokens**.  
[ Go to **API Tokens** ](https://dash.cloudflare.com/profile/api-tokens)
2. Select **Create Token**.
3. Select **Create Custom Token**.
4. Enter a **Token name**, for example `AI Search Python`.
5. Under **Permissions**, add two permissions:

  * **Account** \> **AI Search:Edit**
  * **Account** \> **AI Search:Run**
6. Select **Continue to summary**, then select **Create Token**.
7. Copy and save the token value. This is your `API_TOKEN`.

## 2\. Set up your Python environment

Create a project directory and a virtual environment to isolate your dependencies.

```sh
mkdir ai-search-python && cd ai-search-python
python3 -m venv .venv
source .venv/bin/activate
```

On Windows, activate the virtual environment with `.venv\Scripts\activate` instead.

## 3\. Install the Cloudflare Python SDK

Install the official `cloudflare` package:

```sh
pip install cloudflare
```

## 4\. Set your credentials

Export your account ID and API token as environment variables.

```sh
export CLOUDFLARE_ACCOUNT_ID="<ACCOUNT_ID>"
export CLOUDFLARE_API_TOKEN="<API_TOKEN>"
```

## 5\. Create an AI Search instance

Create a file named `quickstart.py`. The following code sets up a client and creates an instance named `my-instance` in the `default` namespace. Because no data source is specified, the instance uses [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/), so you can upload files to it directly.

**quickstart.py**

```python
import os


from cloudflare import Cloudflare


client = Cloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])
account_id = os.environ["CLOUDFLARE_ACCOUNT_ID"]


instance = client.aisearch.namespaces.instances.create(
    name="default",
    account_id=account_id,
    id="my-instance",
)


print(f"Created instance: {instance.id}")
```

Note

Creating an instance is a one-time action. If you run the script again, remove this step, because an instance name must be unique within a namespace.

## 6\. Upload content

Add the following to `quickstart.py` to upload a document. Setting `wait_for_completion` to `True` waits for indexing before returning so the file is ready to search. If indexing is still finishing, `item.status` may be `running`; the file continues indexing in the background and becomes searchable shortly after.

**quickstart.py**

```python
item = client.aisearch.namespaces.instances.items.upload(
    id="my-instance",
    account_id=account_id,
    name="default",
    file={
        "file": (
            "getting-started.md",
            b"AI Search indexes uploaded content for retrieval.",
            "text/markdown",
        ),
        "wait_for_completion": True,
    },
)


print(f"Uploaded item status: {item.status}")
```

## 7\. Search your instance

Add the following to `quickstart.py` to run a query against your indexed content.

**quickstart.py**

```python
results = client.aisearch.namespaces.instances.search(
    id="my-instance",
    account_id=account_id,
    name="default",
    query="How does AI Search handle uploaded content?",
)


if results.chunks:
    print(results.chunks[0].text)
else:
    print("No results yet — your content may still be indexing. Try again in a moment.")
```

Run the script:

```sh
python quickstart.py
```

If the search returns no results, the content may still be indexing. Wait a moment, then run the search again.

## Next steps

[ REST API ](https://developers.cloudflare.com/ai-search/api/search/rest-api/) Query AI Search using HTTP requests. 

[ Workers API ](https://developers.cloudflare.com/ai-search/get-started/workers/) Query AI Search from within a Cloudflare Worker.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/get-started/python/#page","headline":"Python SDK · Cloudflare AI Search docs","description":"Create, populate, and query an AI Search instance from Python.","url":"https://developers.cloudflare.com/ai-search/get-started/python/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/get-started/","name":"Get started"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/get-started/python/","name":"Python SDK"}}]}
```

---

---
title: Workers binding
description: Create, populate, and query an AI Search instance from a Cloudflare Worker.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Workers binding

This guide walks you through creating and querying an AI Search instance from a [Cloudflare Worker](https://developers.cloudflare.com/workers/) using the Workers Binding. The Workers Binding uses a runtime [API](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) that runs inside a Worker and calls AI Search without managing API tokens.

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

## 1\. Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `ai-search-tutorial` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- ai-search-tutorial
```

```
yarn create cloudflare ai-search-tutorial
```

```
pnpm create cloudflare@latest ai-search-tutorial
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd ai-search-tutorial
```

## 2\. Connect your Worker to AI Search

Create a binding between your Worker and your AI Search instance. [Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform.

Add the following to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/):

* [  wrangler.jsonc ](#tab-panel-7203)
* [  wrangler.toml ](#tab-panel-7204)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true
```

This binds the `default` namespace to `env.AI_SEARCH`. Instances that you create without specifying a namespace belong to the `default` namespace. The `remote` option lets `wrangler dev` proxy requests to your deployed instance, since AI Search does not run locally. For all binding options, refer to the [Workers binding reference](https://developers.cloudflare.com/ai-search/api/search/workers-binding/).

## 3\. Create and query AI Search from your Worker

Update the `src/index.ts` file in your `ai-search-tutorial` directory with the following code. It exposes two routes: `/setup` creates an instance named `my-instance` and indexes a sample document, and the default route queries it.

* [  JavaScript ](#tab-panel-7205)
* [  TypeScript ](#tab-panel-7206)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);


    // Visit /setup once to create an instance and index a sample document.
    if (url.pathname === "/setup") {
      const instance = await env.AI_SEARCH.create({ id: "my-instance" });
      const item = await instance.items.uploadAndPoll(
        "getting-started.md",
        "AI Search indexes uploaded content for retrieval.",
      );
      return Response.json({ created: "my-instance", status: item.status });
    }


    // Query the instance.
    const query = url.searchParams.get("q") ?? "What does AI Search do?";


    const results = await env.AI_SEARCH.get("my-instance").search({
      messages: [{ role: "user", content: query }],
      ai_search_options: {
        retrieval: { max_num_results: 3 },
      },
    });


    return Response.json(results.chunks);
  },
};
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);


    // Visit /setup once to create an instance and index a sample document.
    if (url.pathname === "/setup") {
      const instance = await env.AI_SEARCH.create({ id: "my-instance" });
      const item = await instance.items.uploadAndPoll(
        "getting-started.md",
        "AI Search indexes uploaded content for retrieval.",
      );
      return Response.json({ created: "my-instance", status: item.status });
    }


    // Query the instance.
    const query = url.searchParams.get("q") ?? "What does AI Search do?";


    const results = await env.AI_SEARCH.get("my-instance").search({
      messages: [{ role: "user", content: query }],
      ai_search_options: {
        retrieval: { max_num_results: 3 },
      },
    });


    return Response.json(results.chunks);
  },
} satisfies ExportedHandler<Env>;
```

## 4\. Develop locally

Start a local development server:

```sh
npx wrangler dev
```

Wrangler gives you a URL (usually `localhost:8787`). Visit `/setup` once to create your instance and index the sample document, then query it at `/?q=your+search+terms`.

## 5\. Deploy your Worker

Log in with your Cloudflare account:

```sh
npx wrangler login
```

Deploy your Worker to make it accessible on the Internet:

```sh
npx wrangler deploy
```

```txt
https://ai-search-tutorial.<YOUR_SUBDOMAIN>.workers.dev
```

## Next steps

[ Search Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Full reference for searching and chatting from a Worker. 

[ Items Workers binding ](https://developers.cloudflare.com/ai-search/api/items/workers-binding/) Upload, list, and manage documents from a Worker.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/get-started/workers/#page","headline":"Workers binding · Cloudflare AI Search docs","description":"Create, populate, and query an AI Search instance from a Cloudflare Worker.","url":"https://developers.cloudflare.com/ai-search/get-started/workers/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-03","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/get-started/","name":"Get started"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/get-started/workers/","name":"Workers binding"}}]}
```

---

---
title: CLI
description: Create and manage AI Search instances from the command line.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# CLI

This guide walks you through creating an AI Search instance using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/).

## 1\. Install Wrangler

Install [Wrangler](https://developers.cloudflare.com/workers/wrangler/), the command-line tool for Cloudflare Workers and developer platform products.

 npm  yarn  pnpm  bun 

```
npm install wrangler
```

```
yarn install wrangler
```

```
pnpm install wrangler
```

```
bun install wrangler
```

## 2\. Create an AI Search instance

Create a new instance.

```sh
wrangler ai-search create my-instance
```

You can upload files to the instance using the [dashboard](https://developers.cloudflare.com/ai-search/get-started/dashboard/#upload-content) or the [REST API](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/upload/).

### Connect a data source (optional)

You can optionally connect a website or R2 bucket when creating the instance.

**Website:**

Automatically crawl and index a [website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) that you own.

```sh
wrangler ai-search create my-instance --type web-crawler --source developers.cloudflare.com
```

**R2 bucket:**

Index documents stored in an [R2 bucket](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/).

```sh
wrangler ai-search create my-instance --type r2 --source my-bucket
```

## 3\. Check indexing status

Check if your content has finished indexing by running the `stats` command.

```sh
wrangler ai-search stats my-instance
```

## 4\. Test your instance

Once indexing is complete, run a search query against your instance.

```sh
wrangler ai-search search my-instance --query "What is Cloudflare?"
```

For the full list of available commands, refer to [Wrangler commands](https://developers.cloudflare.com/ai-search/wrangler-commands/).

## Add to your application

[ Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Query AI Search directly from your Workers code. 

[ REST API ](https://developers.cloudflare.com/ai-search/api/search/rest-api/) Query AI Search using HTTP requests.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/get-started/wrangler/#page","headline":"CLI · Cloudflare AI Search docs","description":"Create and manage AI Search instances from the command line.","url":"https://developers.cloudflare.com/ai-search/get-started/wrangler/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/get-started/","name":"Get started"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/get-started/wrangler/","name":"CLI"}}]}
```

---

---
title: Wrangler CLI
description: Manage AI Search instances from the command line using Wrangler.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Wrangler CLI

## `ai-search list`

List all AI Search instances

* [  npm ](#tab-panel-7253)
* [  pnpm ](#tab-panel-7254)
* [  yarn ](#tab-panel-7255)

```sh
npx wrangler ai-search list
```

```sh
pnpm wrangler ai-search list
```

```sh
yarn wrangler ai-search list
```

* `--namespace` ` string ` alias: --n default: default  
The namespace to list instances from.
* `--json` ` boolean ` default: false  
Return output as clean JSON
* `--page` ` number ` default: 1  
Page number of the results, can configure page size using "per-page"
* `--per-page` ` number `  
Number of instances to show per page

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search create`

Create a new AI Search instance

* [  npm ](#tab-panel-7256)
* [  pnpm ](#tab-panel-7257)
* [  yarn ](#tab-panel-7258)

```sh
npx wrangler ai-search create [NAME]
```

```sh
pnpm wrangler ai-search create [NAME]
```

```sh
yarn wrangler ai-search create [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance to create (must be unique within its namespace).
* `--namespace` ` string ` alias: --n  
The namespace to create the instance in.
* `--source` ` string `  
Data source identifier (R2 bucket name or web URL).
* `--type` ` string `  
The source type for the instance.
* `--source-jurisdiction` ` string `  
The R2 jurisdiction of the source bucket (e.g. eu, fedramp). Only valid with --type r2; omit for no specific jurisdiction.
* `--embedding-model` ` string `  
Embedding model to use.
* `--generation-model` ` string `  
LLM model for chat completions.
* `--chunk-size` ` number `  
Chunk size for document splitting (min: 64).
* `--chunk-overlap` ` number `  
Overlap between document chunks.
* `--max-num-results` ` number `  
Maximum search results per query.
* `--reranking` ` boolean `  
Enable reranking of search results.
* `--reranking-model` ` string `  
Model to use for reranking.
* `--hybrid-search` ` boolean `  
Enable hybrid (keyword + vector) search.
* `--cache` ` boolean `  
Enable response caching.
* `--score-threshold` ` number `  
Minimum relevance score threshold (0-1).
* `--prefix` ` string `  
R2 key prefix to scope indexing.
* `--include-items` ` array `  
Glob patterns for items to include.
* `--exclude-items` ` array `  
Glob patterns for items to exclude.
* `--custom-metadata` ` array `  
Custom metadata fields, formatted as 'field\_name:data\_type'. data\_type must be one of: text, number, boolean, datetime. Repeat the flag for multiple fields (e.g. --custom-metadata title:text --custom-metadata views:number).
* `--custom-metadata-schema` ` string `  
Path to a JSON file describing custom metadata fields. The file must contain an array of { "field\_name", "data\_type" } objects. Mutually exclusive with --custom-metadata.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search get`

Get details of an AI Search instance

* [  npm ](#tab-panel-7259)
* [  pnpm ](#tab-panel-7260)
* [  yarn ](#tab-panel-7261)

```sh
npx wrangler ai-search get [NAME]
```

```sh
pnpm wrangler ai-search get [NAME]
```

```sh
yarn wrangler ai-search get [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search update`

Update an AI Search instance configuration

* [  npm ](#tab-panel-7262)
* [  pnpm ](#tab-panel-7263)
* [  yarn ](#tab-panel-7264)

```sh
npx wrangler ai-search update [NAME]
```

```sh
pnpm wrangler ai-search update [NAME]
```

```sh
yarn wrangler ai-search update [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance to update.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--embedding-model` ` string `  
Update the embedding model.
* `--generation-model` ` string `  
Update the LLM model for chat completions.
* `--chunk-size` ` number `  
Update the chunk size.
* `--chunk-overlap` ` number `  
Update the chunk overlap.
* `--max-num-results` ` number `  
Update max search results per query.
* `--reranking` ` boolean `  
Enable or disable reranking.
* `--reranking-model` ` string `  
Update the reranking model.
* `--hybrid-search` ` boolean `  
Enable or disable hybrid search.
* `--cache` ` boolean `  
Enable or disable caching.
* `--score-threshold` ` number `  
Update the minimum relevance score threshold (0-1).
* `--paused` ` boolean `  
Pause or resume the instance.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search delete`

Delete an AI Search instance

* [  npm ](#tab-panel-7265)
* [  pnpm ](#tab-panel-7266)
* [  yarn ](#tab-panel-7267)

```sh
npx wrangler ai-search delete [NAME]
```

```sh
pnpm wrangler ai-search delete [NAME]
```

```sh
yarn wrangler ai-search delete [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance to delete.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--force` ` boolean ` alias: --y default: false  
Skip confirmation

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search stats`

Get usage statistics for an AI Search instance

* [  npm ](#tab-panel-7268)
* [  pnpm ](#tab-panel-7269)
* [  yarn ](#tab-panel-7270)

```sh
npx wrangler ai-search stats [NAME]
```

```sh
pnpm wrangler ai-search stats [NAME]
```

```sh
yarn wrangler ai-search stats [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search search`

Execute a semantic search query against an AI Search instance

* [  npm ](#tab-panel-7271)
* [  pnpm ](#tab-panel-7272)
* [  yarn ](#tab-panel-7273)

```sh
npx wrangler ai-search search [NAME]
```

```sh
pnpm wrangler ai-search search [NAME]
```

```sh
yarn wrangler ai-search search [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--query` ` string ` required  
The search query text.
* `--max-num-results` ` number `  
Override maximum number of results.
* `--score-threshold` ` number `  
Override minimum relevance score (0-1).
* `--reranking` ` boolean `  
Override reranking setting.
* `--filter` ` array `  
Metadata filter as key=value (repeatable, e.g. --filter type=docs --filter lang=en).
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search namespace list`

List all AI Search namespaces

* [  npm ](#tab-panel-7274)
* [  pnpm ](#tab-panel-7275)
* [  yarn ](#tab-panel-7276)

```sh
npx wrangler ai-search namespace list
```

```sh
pnpm wrangler ai-search namespace list
```

```sh
yarn wrangler ai-search namespace list
```

* `--json` ` boolean ` default: false  
Return output as clean JSON
* `--page` ` number ` default: 1  
Page number of the results, can configure page size using "per-page"
* `--per-page` ` number `  
Number of namespaces to show per page
* `--search` ` string `  
Filter namespaces whose name or description contains this string (case-insensitive).

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search namespace create`

Create a new AI Search namespace

* [  npm ](#tab-panel-7277)
* [  pnpm ](#tab-panel-7278)
* [  yarn ](#tab-panel-7279)

```sh
npx wrangler ai-search namespace create [NAME]
```

```sh
pnpm wrangler ai-search namespace create [NAME]
```

```sh
yarn wrangler ai-search namespace create [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search namespace to create.
* `--description` ` string `  
Optional description for the namespace (max 256 chars).
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search namespace get`

Get details of an AI Search namespace

* [  npm ](#tab-panel-7280)
* [  pnpm ](#tab-panel-7281)
* [  yarn ](#tab-panel-7282)

```sh
npx wrangler ai-search namespace get [NAME]
```

```sh
pnpm wrangler ai-search namespace get [NAME]
```

```sh
yarn wrangler ai-search namespace get [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search namespace.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search namespace update`

Update an AI Search namespace

* [  npm ](#tab-panel-7283)
* [  pnpm ](#tab-panel-7284)
* [  yarn ](#tab-panel-7285)

```sh
npx wrangler ai-search namespace update [NAME]
```

```sh
pnpm wrangler ai-search namespace update [NAME]
```

```sh
yarn wrangler ai-search namespace update [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search namespace to update.
* `--description` ` string `  
Updated description for the namespace (max 256 chars).
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search namespace delete`

Delete an AI Search namespace

* [  npm ](#tab-panel-7286)
* [  pnpm ](#tab-panel-7287)
* [  yarn ](#tab-panel-7288)

```sh
npx wrangler ai-search namespace delete [NAME]
```

```sh
pnpm wrangler ai-search namespace delete [NAME]
```

```sh
yarn wrangler ai-search namespace delete [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search namespace to delete.
* `--force` ` boolean ` alias: --y default: false  
Skip confirmation

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search jobs list`

List indexing jobs for an AI Search instance

* [  npm ](#tab-panel-7289)
* [  pnpm ](#tab-panel-7290)
* [  yarn ](#tab-panel-7291)

```sh
npx wrangler ai-search jobs list [NAME]
```

```sh
pnpm wrangler ai-search jobs list [NAME]
```

```sh
yarn wrangler ai-search jobs list [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--json` ` boolean ` default: false  
Return output as clean JSON
* `--page` ` number ` default: 1  
Page number of the results, can configure page size using "per-page"
* `--per-page` ` number `  
Number of jobs to show per page

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search jobs create`

Trigger a new indexing job for an AI Search instance

* [  npm ](#tab-panel-7292)
* [  pnpm ](#tab-panel-7293)
* [  yarn ](#tab-panel-7294)

```sh
npx wrangler ai-search jobs create [NAME]
```

```sh
pnpm wrangler ai-search jobs create [NAME]
```

```sh
yarn wrangler ai-search jobs create [NAME]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--description` ` string `  
Optional description for the indexing job.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search jobs get`

Get details of an AI Search indexing job

* [  npm ](#tab-panel-7295)
* [  pnpm ](#tab-panel-7296)
* [  yarn ](#tab-panel-7297)

```sh
npx wrangler ai-search jobs get [NAME] [JOB-ID]
```

```sh
pnpm wrangler ai-search jobs get [NAME] [JOB-ID]
```

```sh
yarn wrangler ai-search jobs get [NAME] [JOB-ID]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `[JOB-ID]` ` string ` required  
The ID of the indexing job.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--json` ` boolean ` default: false  
Return output as clean JSON

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search jobs cancel`

Cancel an in-progress AI Search indexing job

* [  npm ](#tab-panel-7298)
* [  pnpm ](#tab-panel-7299)
* [  yarn ](#tab-panel-7300)

```sh
npx wrangler ai-search jobs cancel [NAME] [JOB-ID]
```

```sh
pnpm wrangler ai-search jobs cancel [NAME] [JOB-ID]
```

```sh
yarn wrangler ai-search jobs cancel [NAME] [JOB-ID]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `[JOB-ID]` ` string ` required  
The ID of the indexing job to cancel.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--force` ` boolean ` alias: --y default: false  
Skip confirmation

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

## `ai-search jobs logs`

List log entries for an AI Search indexing job

* [  npm ](#tab-panel-7301)
* [  pnpm ](#tab-panel-7302)
* [  yarn ](#tab-panel-7303)

```sh
npx wrangler ai-search jobs logs [NAME] [JOB-ID]
```

```sh
pnpm wrangler ai-search jobs logs [NAME] [JOB-ID]
```

```sh
yarn wrangler ai-search jobs logs [NAME] [JOB-ID]
```

* `[NAME]` ` string ` required  
The name of the AI Search instance.
* `[JOB-ID]` ` string ` required  
The ID of the indexing job.
* `--namespace` ` string ` alias: --n default: default  
The namespace the instance belongs to.
* `--json` ` boolean ` default: false  
Return output as clean JSON
* `--page` ` number ` default: 1  
Page number of the results, can configure page size using "per-page"
* `--per-page` ` number `  
Number of log entries to show per page

Global flags

* `--v` ` boolean ` alias: --version  
Show version number
* `--cwd` ` string `  
Run as if Wrangler was started in the specified directory instead of the current working directory
* `--config` ` string ` alias: --c  
Path to Wrangler configuration file
* `--env` ` string ` alias: --e  
Environment to use for operations, and for selecting .env and .dev.vars files
* `--env-file` ` string `  
Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files
* `--experimental-provision` ` boolean ` aliases: --x-provision default: true  
Experimental: Enable automatic resource provisioning
* `--experimental-auto-create` ` boolean ` alias: --x-auto-create default: true  
Automatically provision draft bindings with new resources
* `--install-skills` ` boolean ` default: false  
Install Cloudflare skills for detected AI coding agents before running the command
* `--profile` ` string `  
Use a specific auth profile

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/wrangler-commands/#page","headline":"Wrangler CLI · Cloudflare AI Search docs","description":"Manage AI Search instances from the command line using Wrangler.","url":"https://developers.cloudflare.com/ai-search/wrangler-commands/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/wrangler-commands/","name":"Wrangler CLI"}}]}
```

---

---
title: Configuration
description: Customize how your AI Search instance indexes data, retrieves results, and generates responses.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Configuration

You can customize how your AI Search instance indexes your data, retrieves results, and generates responses. Some settings can be updated after the instance is created, while others are fixed at creation time.

## Data source

| Configuration                                                                                               | Editable after creation | Description                                              |
| ----------------------------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------------------------- |
| [Built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) | n/a                     | Upload files directly to an instance                     |
| [Website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/)                   | no                      | Connect a domain you own to index website pages          |
| [R2 Bucket](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/)                      | no                      | Connect a Cloudflare R2 bucket to index stored documents |

## Indexing

| Configuration                                                                                              | Editable after creation | Description                                                     |
| ---------------------------------------------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------- |
| [Vector search](https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/)         | yes                     | Vector search and the built-in vector index                     |
| [Path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/)       | yes                     | Include or exclude specific paths from indexing                 |
| [Chunking](https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/)                   | yes                     | Number of tokens per chunk and overlap between chunks           |
| [Syncing](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/)                     | yes                     | Sync jobs and indexing controls                                 |
| [Keyword search](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/)       | yes                     | Enable keyword (BM25) search for exact term matching            |
| [Hybrid search](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/)         | yes                     | Combine vector and keyword search with configurable fusion      |
| [Metadata attributes](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/)        | yes                     | Define built-in and custom metadata fields                      |
| [Service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) | yes                     | API token that grants AI Search permission to access R2 buckets |

## Retrieval

| Configuration                                                                                             | Editable after creation | Description                                                   |
| --------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------- |
| [Result controls](https://developers.cloudflare.com/ai-search/configuration/retrieval/result-controls/)   | yes                     | Match threshold and maximum number of results                 |
| [Filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/)               | yes                     | Filter results by metadata attributes                         |
| [Relevance boosting](https://developers.cloudflare.com/ai-search/configuration/retrieval/boosting/)       | yes                     | Bias results by metadata characteristics                      |
| [Reranking](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/)               | yes                     | Reorder results by semantic relevance using a reranking model |
| [Query rewriting](https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/)   | yes                     | Rewrite follow-up queries using conversation context          |
| [System prompt](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/)       | yes                     | Guide query rewriting and response generation behavior        |
| [Similarity caching](https://developers.cloudflare.com/ai-search/configuration/retrieval/cache/)          | yes                     | Cache responses for similar prompts                           |
| [Public endpoint](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/)   | yes                     | Enable public access to search, chat, and MCP endpoints       |
| [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/) | yes                     | Embed pre-built search and chat components in your website    |

## Models

| Configuration                                                                              | Editable after creation | Description                                         |
| ------------------------------------------------------------------------------------------ | ----------------------- | --------------------------------------------------- |
| [Embedding model](https://developers.cloudflare.com/ai-search/configuration/models/)       | no                      | Model used to generate vector embeddings            |
| [Generation model](https://developers.cloudflare.com/ai-search/configuration/models/)      | yes                     | Model used to generate the final response           |
| [Query rewriting model](https://developers.cloudflare.com/ai-search/configuration/models/) | yes                     | Model used for query rewriting                      |
| [Reranking model](https://developers.cloudflare.com/ai-search/configuration/models/)       | yes                     | Model used to reorder results by semantic relevance |
| [AI Gateway](https://developers.cloudflare.com/ai-search/configuration/models/ai-gateway/) | yes                     | Observe and control the model calls AI Search makes |

```json
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/ai-search/configuration/#page","headline":"Configuration · Cloudflare AI Search docs","description":"Customize how your AI Search instance indexes data, retrieves results, and generates responses.","url":"https://developers.cloudflare.com/ai-search/configuration/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-06","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}}]}
```

---

---
title: Data source
description: Connect a website, R2 bucket, or upload files directly to your AI Search instance for indexing.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Data source

You can upload files directly to an instance or connect an external data source.

| Data Source                                                                                                 | Description                                                                   |
| ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [Built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) | Upload files directly to an instance. Available by default on every instance. |
| [Website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/)                   | Connect a domain you own to index website pages.                              |
| [R2 Bucket](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/)                      | Connect a Cloudflare R2 bucket to index stored documents.                     |

## Supported file types

AI Search can ingest a variety of file types. The following plain text files and rich format files are supported.

### Plain text file types

| Format     | File extensions                                                  | Mime Type                                                      |
| ---------- | ---------------------------------------------------------------- | -------------------------------------------------------------- |
| Text       | .txt, .rst                                                       | text/plain                                                     |
| Log        | .log, .log.gz                                                    | text/plain                                                     |
| Config     | .ini, .conf, .env, .properties, .gitignore, .editorconfig, .toml | text/plain, text/toml                                          |
| Markdown   | .markdown, .md, .mdx, .mdoc                                      | text/markdown                                                  |
| LaTeX      | .tex, .latex                                                     | application/x-tex, application/x-latex                         |
| Script     | .sh, .bat, .ps1                                                  | application/x-sh, application/x-msdos-batch, text/x-powershell |
| SGML       | .sgml                                                            | text/sgml                                                      |
| JSON       | .json                                                            | application/json                                               |
| SQL        | .sql                                                             | application/sql                                                |
| YAML       | .yaml, .yml                                                      | application/x-yaml                                             |
| CSS        | .css                                                             | text/css                                                       |
| JavaScript | .js                                                              | application/javascript                                         |
| PHP        | .php                                                             | application/x-httpd-php                                        |
| Python     | .py                                                              | text/x-python                                                  |
| Ruby       | .rb                                                              | text/x-ruby                                                    |
| Java       | .java                                                            | text/x-java-source                                             |
| C          | .c                                                               | text/x-c                                                       |
| C++        | .cpp, .cxx                                                       | text/x-c++                                                     |
| C Header   | .h, .hpp                                                         | text/x-c-header                                                |
| Go         | .go                                                              | text/x-go                                                      |
| Rust       | .rs                                                              | text/rust                                                      |
| Swift      | .swift                                                           | text/swift                                                     |
| Dart       | .dart                                                            | text/dart                                                      |
| EMACS Lisp | .el                                                              | application/x-elisp, text/x-elisp, text/x-emacs-lisp           |

### Rich format file types

AI Search uses [Markdown Conversion](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/) to convert rich format files to markdown. The following table lists the supported formats that will be converted to Markdown:

| Format                     | File extensions                            | Mime Types                                                                                                                                                                                                                                                                  | |  PDF Documents | .pdf | application/pdf |
| -------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---- | --------------- |
| Images 1                   | .jpeg, .jpg, .png, .webp, .svg, .gif, .bmp | image/jpeg, image/png, image/webp, image/svg+xml, image/gif, image/bmp                                                                                                                                                                                                      |                  |      |                 |
| HTML Documents             | .html, .htm                                | text/html                                                                                                                                                                                                                                                                   |                  |      |                 |
| XML Documents              | .xml                                       | application/xml                                                                                                                                                                                                                                                             |                  |      |                 |
| Microsoft Office Documents | .xlsx, .xlsm, .xlsb, .xls, .et, .docx      | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel.sheet.macroenabled.12, application/vnd.ms-excel.sheet.binary.macroenabled.12, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.wordprocessingml.document |                  |      |                 |
| Open Document Format       | .ods, .odt                                 | application/vnd.oasis.opendocument.spreadsheet, application/vnd.oasis.opendocument.text                                                                                                                                                                                     |                  |      |                 |
| CSV                        | .csv                                       | text/csv                                                                                                                                                                                                                                                                    |                  |      |                 |
| Apple Documents            | .numbers                                   | application/vnd.apple.numbers                                                                                                                                                                                                                                               |                  |      |                 |

1 Image conversion uses two Workers AI models for object detection and summarization. See [Workers AI pricing](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/#pricing) for more details.

## File limits

AI Search has a file size limit of **up to 4 MB**.

Files that exceed this limit will not be indexed and will show up in the error logs.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/data-source/#page","headline":"Data source · Cloudflare AI Search docs","description":"Connect a website, R2 bucket, or upload files directly to your AI Search instance for indexing.","url":"https://developers.cloudflare.com/ai-search/configuration/data-source/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-24","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/data-source/","name":"Data source"}}]}
```

---

---
title: Built-in storage
description: Upload files directly to an AI Search instance using built-in storage powered by R2 and Vectorize.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Built-in storage

Every AI Search instance comes with built-in storage and a built-in vector index, powered by [R2](https://developers.cloudflare.com/r2/) and [Vectorize](https://developers.cloudflare.com/vectorize/). You can upload files directly to an instance without setting up either service yourself.

## Upload and manage files

Upload files to an instance using the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/) (Workers binding or REST API) or the **Items** tab in the dashboard (**AI** \> **AI Search** \> your instance > **Items**). You can also list, view, and delete uploaded files through the Items API or the dashboard.

For supported file types, refer to [Supported file types](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types).

## Indexing

Files uploaded to built-in storage are indexed immediately. External data sources like websites and R2 buckets are indexed on a [sync schedule](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/).

## External data sources

An instance can use built-in storage alongside an external data source. The available external data sources are:

* [Website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/): crawl and index a website that you own
* [R2 Bucket](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/): index documents stored in a Cloudflare R2 bucket

For example, an instance can be backed by a website for shared documentation while also accepting file uploads through the Items API for additional content.

## Limits and pricing

Storage, vector indexing, and Browser Run usage for crawling are included. Workers AI and AI Gateway usage is billed separately. For full details, refer to [Limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/#page","headline":"Built-in storage · Cloudflare AI Search docs","description":"Upload files directly to an AI Search instance using built-in storage powered by R2 and Vectorize.","url":"https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-19","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/data-source/","name":"Data source"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/data-source/built-in-storage/","name":"Built-in storage"}}]}
```

---

---
title: R2
description: Connect a Cloudflare R2 bucket as a data source to index stored documents with AI Search.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# R2

You can connect a [Cloudflare R2](https://developers.cloudflare.com/r2/) bucket as a data source for your AI Search instance. AI Search indexes the files stored in your bucket automatically.

## Get started

You can connect an R2 bucket when creating a new instance through the [dashboard](https://developers.cloudflare.com/ai-search/get-started/dashboard/), the [REST API](https://developers.cloudflare.com/ai-search/get-started/api/), or [Wrangler](https://developers.cloudflare.com/ai-search/get-started/wrangler/). R2 is an optional data source that you can add alongside [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/).

If you have never created an R2-backed instance before, we recommend using the dashboard or Wrangler CLI, which will create and register a [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) for you automatically. If you are using the REST API or Workers binding, you will need to create a service API token and pass the `token_id` in the create request. Refer to [Service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) for setup instructions.

To get started, [configure an R2 bucket](https://developers.cloudflare.com/r2/get-started/) containing your data. Files that are unsupported or exceed the size limit will be skipped during indexing and logged as errors.

## Path filtering

You can control which files get indexed by defining include and exclude rules for object paths. Use this to limit indexing to specific folders or to exclude files you do not want searchable.

For example, to index only documentation while excluding drafts:

* **Include:** `/docs/**`
* **Exclude:** `/docs/drafts/**`

Refer to [Path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/) for pattern syntax, filtering behavior, and more examples.

For supported file types and size limits, refer to [Data source](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types).

## Custom metadata

You can attach custom metadata to R2 objects for filtering search results. AI Search reads metadata from S3-compatible custom headers (`x-amz-meta-*`).

Before metadata can be extracted, you must [define a schema](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/#define-a-schema) in your AI Search configuration.

### Set metadata when uploading

* [ Workers R2 binding ](#tab-panel-7198)
* [ AWS SDK ](#tab-panel-7199)
* [ Wrangler CLI ](#tab-panel-7200)

Use the `customMetadata` option when uploading objects with the [R2 Workers binding](https://developers.cloudflare.com/r2/api/workers/workers-api-usage/):

**JavaScript**

```js
await env.MY_BUCKET.put("docs/document.pdf", fileContent, {
  customMetadata: {
    category: "documentation",
    version: "2.5",
    is_public: "true",
  },
});
```

Use the `Metadata` option with the [AWS SDK for JavaScript](https://developers.cloudflare.com/r2/api/s3/api/):

**JavaScript**

```js
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";


const client = new S3Client({
  region: "auto",
  endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
  credentials: {
    accessKeyId: R2_ACCESS_KEY_ID,
    secretAccessKey: R2_SECRET_ACCESS_KEY,
  },
});


await client.send(
  new PutObjectCommand({
    Bucket: "your-bucket",
    Key: "docs/document.pdf",
    Body: fileContent,
    Metadata: {
      category: "documentation",
      version: "2.5",
      is_public: "true",
    },
  }),
);
```

Use the `--header` flag with [Wrangler](https://developers.cloudflare.com/r2/reference/wrangler-commands/) to set `x-amz-meta-*` headers:

```sh
wrangler r2 object put your-bucket/docs/document.pdf \
  --file=./document.pdf \
  --header="x-amz-meta-category:documentation" \
  --header="x-amz-meta-version:2.5" \
  --header="x-amz-meta-is_public:true"
```

### How metadata extraction works

When a file is fetched from R2 during indexing:

1. All `x-amz-meta-*` headers are read from the object.
2. The `x-amz-meta-` prefix is stripped (for example, `x-amz-meta-category` becomes `category`).
3. Field names are matched against your schema (case-insensitive).
4. Values are cast to the configured data type.
5. Invalid values (for example, a non-numeric string for a `number` type) are silently ignored.

### Unicode support

Metadata values support Unicode characters through MIME-Word encoding (RFC 2047). Most S3-compatible tools handle this encoding automatically.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/data-source/r2/#page","headline":"R2 · Cloudflare AI Search docs","description":"Connect a Cloudflare R2 bucket as a data source to index stored documents with AI Search.","url":"https://developers.cloudflare.com/ai-search/configuration/data-source/r2/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/data-source/","name":"Data source"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/data-source/r2/","name":"R2"}}]}
```

---

---
title: Website
description: Connect a domain you own as a data source so AI Search can crawl and index your website pages.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Website

You can connect a website you own as a data source for your AI Search instance. AI Search crawls and indexes the pages automatically.

You can only crawl domains that you have onboarded onto the same Cloudflare account. Refer to [Onboard a domain](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/) for more information on adding a domain to your Cloudflare account.

Bot protection may block crawling

If you use Cloudflare products that control or restrict bot traffic such as [Bot Management](https://developers.cloudflare.com/bots/), [Web Application Firewall (WAF)](https://developers.cloudflare.com/waf/), or [Turnstile](https://developers.cloudflare.com/turnstile/), the same rules will apply to the AI Search crawler. Make sure to configure an exception or an allow-list for the AI Search crawler in your settings.

## Get started

You can connect a website when creating a new instance through the [dashboard](https://developers.cloudflare.com/ai-search/get-started/dashboard/), the [REST API](https://developers.cloudflare.com/ai-search/get-started/api/), or [Wrangler](https://developers.cloudflare.com/ai-search/get-started/wrangler/). Website is an optional data source that you can add alongside [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/).

## How website crawling works

When you connect a domain, the crawler looks for your website's sitemap to determine which pages to visit:

1. If you configure one or more custom sitemap URLs in the dashboard under **Parser options** \> **Specific sitemap**, AI Search crawls only those sitemap URLs.
2. Otherwise, the crawler checks `robots.txt` for listed sitemaps.
3. If no `robots.txt` is found, the crawler checks for a sitemap at `/sitemap.xml`.
4. If no sitemap is available, the domain cannot be crawled.

### Indexing order

If your sitemaps include `<priority>` attributes, AI Search reads all sitemaps and indexes pages based on each page's priority value, regardless of which sitemap the page is in.

If no `<priority>` is specified, pages are indexed in the order the sitemaps are provided, either from the configured custom sitemap URLs or from `robots.txt` from top to bottom.

AI Search supports `.gz` compressed sitemaps. Both `robots.txt` and sitemaps can use partial URLs.

### Sync and updates

During scheduled or manual [sync jobs](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/), the crawler will check for changes to the `<lastmod>` attribute in your sitemap. If it has been changed to a date occurring after the last sync date, then the page is crawled, the updated version is stored, and the page is automatically reindexed so that your search results always reflect the latest content.

If the `<lastmod>` attribute is not defined, AI Search uses the `<changefreq>` attribute to determine how often to re-crawl the URL. If neither `<lastmod>` nor `<changefreq>` is defined, AI Search automatically crawls each link once a day.

## Storage

Crawled pages are stored in built-in storage automatically.

To see the items parsed from your website, [list the instance's items](https://developers.cloudflare.com/ai-search/api/items/workers-binding/#itemslist) with the Items API, or open the **Items** tab in the dashboard (**AI** \> **AI Search** \> your instance > **Items**).

## Configuration

### Path filtering

You can control which pages get indexed by defining include and exclude rules for URL paths. Use this to limit indexing to specific sections of your site or to exclude content you do not want searchable.

Note

Path filtering matches against the full URL, including the scheme, hostname, and subdomains. For example, a page at `https://www.example.com/blog/post` requires a pattern like `**/blog/**` to match. Using `/blog/**` alone will not match because it does not account for the hostname.

For example, to index only blog posts while excluding drafts:

* **Include:** `**/blog/**`
* **Exclude:** `**/blog/drafts/**`

Refer to [Path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/) for pattern syntax, filtering behavior, and more examples.

For supported file types and size limits, refer to [Data source](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types).

### Parsing options

You can configure parsing options during onboarding or in your instance settings under **Parser options**.

#### Specific sitemap

By default, AI Search crawls all sitemaps listed in your `robots.txt` in the order they appear (top to bottom). If you do not want the crawler to index everything, or if your sitemap is hosted at a non-standard path, you can configure custom sitemap URLs in the dashboard under **Parser options** \> **Specific sitemap**.

When custom sitemap URLs are configured, AI Search uses those sitemap URLs instead of auto-discovering sitemaps from `robots.txt` or `/sitemap.xml`. You can add up to five sitemap URLs.

#### Rendering mode

You can choose how pages are parsed during crawling:

* **Static sites**: Downloads the raw HTML for each page.
* **Rendered sites**: Loads pages with a headless browser and downloads the fully rendered version, including dynamic JavaScript content.

### Extra headers

If your website has pages behind authentication or pages that are only visible to logged-in users, you can configure custom HTTP headers to allow the AI Search crawler to access this protected content. You can add up to five custom HTTP headers to the requests AI Search sends when crawling your site.

#### Providing access to sites protected by Cloudflare Access

To allow AI Search to crawl a site protected by [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/access-controls/), you need to create service token credentials and configure them as custom headers.

Service tokens bypass user authentication, so ensure your Access policies are configured appropriately for the content you want to index. The service token will allow the AI Search crawler to access all content covered by the Service Auth policy.

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), [create a service token](https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token). Once the Client ID and Client Secret are generated, save them for the next steps. For example they can look like:  
```plaintext  
CF-Access-Client-Id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.access  
CF-Access-Client-Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  
```
2. [Create a policy](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/policy-management/#create-a-policy) with the following configuration:

  * Add an **Include** rule with **Selector** set to **Service token**.
  * In **Value**, select the Service Token you created in step 1.
3. [Add your self-hosted application to Access](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) with the following configuration:

  * In Access policies, click **Select existing policies**.
  * Select the policy that you have just created and select **Confirm**.
4. In the Cloudflare dashboard, go to the **AI Search** page.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
5. Select **Create**.
6. Select **Website** as your data source.
7. Under **Parse options**, locate **Extra headers** and add the following two headers using your saved credentials:

  * Header 1:  
    * **Key**: `CF-Access-Client-Id`
    * **Value**: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.access`
  * Header 2:  
    * **Key**: `CF-Access-Client-Secret`
    * **Value**: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
8. Complete the AI Search setup process to create your search instance.

## Custom metadata

You can attach custom metadata to web pages using HTML `<meta>` tags. AI Search extracts metadata from the `<head>` section of each crawled page.

Before custom metadata can be extracted, you must [define a schema](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/#define-a-schema) in your AI Search configuration.

### Add metadata to web pages

Add `<meta>` tags using either the `name` or `property` attribute:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta name="title" content="Getting Started Guide" />
    <meta name="description" content="Learn how to set up the application" />
    <meta property="og:title" content="Getting Started Guide" />
    <meta property="og:image" content="https://example.com/og-image.png" />
    <meta name="category" content="documentation" />
    <meta name="version" content="2.5" />
    <meta name="is_public" content="true" />
  </head>
  <body>
    <!-- Page content -->
  </body>
</html>
```

### Recognized fields

For the following fields, AI Search knows which meta tags to extract from. You must still define these in your schema to enable extraction.

| Field       | Source                                                        |
| ----------- | ------------------------------------------------------------- |
| title       | <meta name="title"> or <meta property="og:title">             |
| description | <meta name="description"> or <meta property="og:description"> |
| image       | <meta property="og:image">                                    |

When both a standard meta tag and an Open Graph tag are present, the standard meta tag takes precedence.

### How metadata extraction works

When the crawler fetches a page:

1. All `<meta>` tags with `name` or `property` attributes are parsed from the `<head>` section.
2. Tag names are matched against your schema (case-insensitive).
3. The `content` attribute value is cast to the configured data type.
4. Extracted metadata is stored alongside the cached HTML.
5. On subsequent processing, metadata flows into the vector index.

### Boolean value parsing

For `boolean` fields, the following values are accepted (case-insensitive):

| True values  | False values |
| ------------ | ------------ |
| true, 1, yes | false, 0, no |

Any other value is treated as invalid and the field is omitted.

## Content selectors

Content selectors let you control which parts of a crawled page are indexed. Each entry pairs a URL glob pattern with a CSS selector. When a page URL matches a glob pattern, only the elements matching the corresponding CSS selector — and their descendants — are extracted and converted to Markdown for indexing.

The list is ordered and the **first matching path wins**. If a page URL matches multiple glob patterns, only the selector from the first match is applied. Order your entries from most specific to least specific.

### Default behavior

Without content selectors, AI Search applies a default processing pipeline that removes elements such as `<header>`, `<footer>`, and `<head>` before converting the remaining content to Markdown. For more details on how HTML is processed, refer to [How HTML is processed](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/how-it-works/#html).

### Configure content selectors in the dashboard

1. Go to the [AI Search ↗](https://dash.cloudflare.com/?to=/:account/ai/ai-search) page in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your AI Search instance, or select **Create** to create a new one with a **Website** data source.
3. Under the data source settings, locate the **Content selectors** section.
4. Select **Add selector**.
5. In the **Path** field, enter a glob pattern to match page URLs. For example, `**/blog/**`.
6. In the **Selector** field, enter a CSS selector to extract content from matching pages. For example, `article .post-body`.
7. To add more entries, select **Add selector** again. Entries are evaluated in order from top to bottom.

### Configure content selectors via the API

Content selectors are configured in the `source_params.web_crawler.parse_options.content_selector` field when creating or updating an AI Search instance. The field accepts an array of objects, each with a `path` and `selector` property.

```bash
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/ai-search/instances" \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-ai-search",
    "source": "https://example.com",
    "type": "web-crawler",
    "source_params": {
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article .post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main .content"
            }
          ]
        }
      }
    }
  }'
```

| Field    | Type   | Description                                                                                                                                                                                                                                                         |
| -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| path     | string | Glob pattern to match against the full page URL. Uses the same glob syntax as [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/) — \* matches within a segment, \*\* crosses directories. Maximum 200 characters. |
| selector | string | CSS selector to extract content from pages matching the path pattern. Supports standard CSS selectors including element, class, ID, and attribute selectors. Maximum 200 characters.                                                                                |

### Examples

#### Extract main content from blog pages

To index only the article body on blog pages and ignore navigation, sidebars, and footers:

| Path           | Selector           |
| -------------- | ------------------ |
| \*\*/blog/\*\* | article .post-body |

#### Target documentation content

To index the main content area of a documentation site:

| Path           | Selector      |
| -------------- | ------------- |
| \*\*/docs/\*\* | main .content |

#### Different selectors for different sections

You can define multiple entries to apply different selectors to different parts of your site. The first matching path wins, so place more specific patterns first:

| Path                    | Selector           |
| ----------------------- | ------------------ |
| \*\*/blog/releases/\*\* | .release-notes     |
| \*\*/blog/\*\*          | article .post-body |
| \*\*/docs/\*\*          | main .content      |

In this example, a page at `https://example.com/blog/releases/v2` matches the first pattern and uses the `.release-notes` selector. A page at `https://example.com/blog/my-post` skips the first pattern and matches the second.

Warning

If a CSS selector does not match any elements on a page, the resulting Markdown is empty and AI Search marks the item as errored. Verify that your selectors match the expected elements before applying them to a broad set of pages.

### Interaction with other features

* **Path filtering**: [Path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/) takes priority over content selectors. Pages excluded by path filters are never crawled, so content selectors do not apply to them.
* **Rendering mode**: Content selectors apply to the HTML that AI Search receives. For sites that render content with JavaScript, use [Rendered sites](#rendering-mode) mode so that selectors can target the fully rendered DOM.
* **Automatic re-indexing**: Updating content selectors triggers a new [sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/) immediately, so changes are applied to all indexed pages.

### Limits

| Limit                            | Value          |
| -------------------------------- | -------------- |
| Maximum content selector entries | 10             |
| Maximum path pattern length      | 200 characters |
| Maximum selector length          | 200 characters |

## Best practices for robots.txt and sitemap

Configure your `robots.txt` and sitemap to help AI Search crawl your site efficiently.

### robots.txt

The AI Search crawler uses the user agent `Cloudflare-AI-Search`. Your `robots.txt` file should reference your sitemap and allow the crawler:

**robots.txt**

```txt
User-agent: *
Allow: /


Sitemap: https://example.com/sitemap.xml
```

You can list multiple sitemaps or use a sitemap index file:

**robots.txt**

```txt
User-agent: *
Allow: /


Sitemap: https://example.com/sitemap.xml
Sitemap: https://example.com/blog-sitemap.xml
Sitemap: https://example.com/sitemap.xml.gz
```

To block all other crawlers but allow only AI Search:

**robots.txt**

```txt
User-agent: *
Disallow: /


User-agent: Cloudflare-AI-Search
Allow: /


Sitemap: https://example.com/sitemap.xml
```

### Sitemap

Structure your sitemap to give AI Search the information it needs to crawl efficiently:

**sitemap.xml**

```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/important-page</loc>
    <lastmod>2026-01-15</lastmod>
    <changefreq>weekly</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://example.com/other-page</loc>
    <lastmod>2026-01-10</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.5</priority>
  </url>
</urlset>
```

Use these attributes to control crawling behavior:

| Attribute    | Purpose                       | Recommendation                                                                                      |
| ------------ | ----------------------------- | --------------------------------------------------------------------------------------------------- |
| <loc>        | URL of the page               | Required. Use full or partial URLs.                                                                 |
| <lastmod>    | Last modification date        | Include to enable change detection. AI Search re-crawls pages when this date changes.               |
| <changefreq> | Expected change frequency     | Use when <lastmod> is not available. Values: always, hourly, daily, weekly, monthly, yearly, never. |
| <priority>   | Relative importance (0.0-1.0) | Set higher values for important pages. AI Search indexes pages in priority order.                   |

You can also use a Sitemap Index to bundle other domain-specific sitemaps:

**sitemap-index.xml**

```xml
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://www.example.com/sitemap-blog.xml</loc>
    <lastmod>2024-08-15T10:00:00+00:00</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://www.example.com/sitemap-docs.xml</loc>
    <lastmod>2024-08-10T12:00:00+00:00</lastmod>
  </sitemap>
</sitemapindex>
```

When parsing a Sitemap Index, AI Search collects all child sitemaps and then crawls them recursively, collecting all relevant URLs present in your sitemaps.

### Recommendations

* Include `<lastmod>` on all URLs to enable efficient change detection during syncs.
* Set `<priority>` to control indexing order. Pages with higher priority are indexed first.
* Use `<changefreq>` as a fallback when `<lastmod>` is not available.
* Use sitemap index files for large sites with multiple sitemaps.
* Compress large sitemaps using `.gz` format to reduce bandwidth.
* Keep sitemaps under 50MB and 50,000 URLs per file (standard sitemap limits).

## Allow the AI Search crawler through WAF

If you have Security rules configured to block bot activity, you can add a rule to allowlist the crawler bot.

1. In the Cloudflare dashboard, go to the **Security rules** page.  
[ Go to **Security rules** ](https://dash.cloudflare.com/?to=/:account/:zone/security/security-rules)
2. To create a new empty rule, select **Create rule** \> **Custom rules**.
3. Enter a descriptive name for the rule in **Rule name**, such as `Allow AI Search`.
4. Under **When incoming requests match**, use the **Field** drop-down list to choose _Bot Detection ID_. For **Operator**, select _equals_. For **Value**, enter `122933950`.
5. Under **Then take action**, in the **Choose action** dropdown, choose _Skip_.
6. Under **Place at**, select the order of the rule in the **Select order** dropdown to be _First_. Setting the order as _First_ allows this rule to be applied before subsequent rules.
7. To save and deploy your rule, select **Deploy**.

## Limits and pricing

The regular AI Search [limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/) apply when using the Website data source.

The crawler will download and index pages only up to the maximum object limit supported for an AI Search instance, and it processes the first set of pages it visits until that limit is reached. In addition, any files that are downloaded but exceed the file size limit will not be indexed.

Browser Run and storage are included for AI Search crawling. For full pricing details, refer to [Limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/data-source/website/#page","headline":"Website · Cloudflare AI Search docs","description":"Connect a domain you own as a data source so AI Search can crawl and index your website pages.","url":"https://developers.cloudflare.com/ai-search/configuration/data-source/website/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-19","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/data-source/","name":"Data source"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/data-source/website/","name":"Website"}}]}
```

---

---
title: Chunking
description: Configure how AI Search splits content into chunks for embedding and retrieval.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Chunking

Chunking is the process of splitting large data into smaller segments before embedding them for search. AI Search uses **recursive chunking**, which breaks your content at natural boundaries (like paragraphs or sentences), and then further splits it if the chunks are too large.

## What is recursive chunking

Recursive chunking tries to keep chunks meaningful by:

* **Splitting at natural boundaries:** like paragraphs, then sentences.
* **Checking the size:** if a chunk is too long (based on token count), it’s split again into smaller parts.

This way, chunks are easy to embed and retrieve, without cutting off thoughts mid-sentence.

## Chunking controls

AI Search exposes two parameters to help you control chunking behavior:

* **Chunk size**: The number of tokens per chunk. The option range may vary depending on the model.
* **Chunk overlap**: The percentage of overlapping tokens between adjacent chunks.  
  * Minimum: `0%`
  * Maximum: `30%`

These settings apply during the indexing step, before your data is embedded and stored in your search index.

## Choosing chunk size and overlap

Chunking affects both how your content is retrieved and how much context is passed into the generation model. Try out this external [chunk visualizer tool ↗](https://huggingface.co/spaces/m-ric/chunk%5Fvisualizer) to help understand how different chunk settings could look.

### Additional considerations:

* **Index size:** Smaller chunk sizes produce more chunks and more total vectors. Refer to the [AI Search limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/) to ensure your configuration stays within instance limits.
* **Generation model context window:** Generation models have a limited context window that must fit all retrieved chunks (`max_num_results` × `chunk size`), the user query, and the model's output. Be careful with large chunks or high `max_num_results` values to avoid context overflows.
* **Cost and performance:** Larger chunks and higher `max_num_results` settings result in more tokens passed to the model, which can increase latency and cost. You can monitor this usage in [AI Gateway](https://developers.cloudflare.com/ai-gateway/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/#page","headline":"Chunking · Cloudflare AI Search docs","description":"Configure how AI Search splits content into chunks for embedding and retrieval.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/chunking/","name":"Chunking"}}]}
```

---

---
title: Hybrid search
description: Combine vector and keyword search in AI Search for broader, more accurate retrieval results.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Hybrid search

Hybrid search runs vector and keyword search in parallel and merges the results. It requires [keyword search](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) to be enabled. For an overview of search modes, refer to [Search modes](https://developers.cloudflare.com/ai-search/concepts/search-modes/).

## Enable hybrid search

Set both `index_method.vector` and `index_method.keyword` to `true`:

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "my-instance",
  index_method: {
    vector: true,
    keyword: true,
  },
  fusion_method: "rrf",
});
```

To disable hybrid search, set `index_method.keyword` to `false`. The keyword index is deleted.

For each search method, you can configure the following to adjust retrieval behavior:

* **Vector search**: Configure the [embedding model](https://developers.cloudflare.com/ai-search/configuration/models/).
* **Keyword search**: Configure the [tokenizer and match mode](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/).

## Fusion method

The `fusion_method` field controls how vector and keyword results are merged.

| Value | Default | Description                                                                                                               |
| ----- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| rrf   | Yes     | Reciprocal Rank Fusion. Scores results based on rank position across both search methods. Recommended for most use cases. |
| max   | No      | Takes the higher of the normalized vector and keyword scores. Use when one search method is consistently more relevant.   |

## Reranking

After fusion, you can optionally apply reranking to further reorder results by semantic relevance. Reranking uses a cross-encoder model that evaluates the query and each chunk together, which can improve precision beyond what fusion alone provides.

Reranking is disabled by default. Refer to [Reranking](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/) to enable and configure it.

## Per-request overrides

Override search settings on individual requests using `ai_search_options.retrieval`.

| Field           | Type                             | Description                                                          |
| --------------- | -------------------------------- | -------------------------------------------------------------------- |
| retrieval\_type | "vector", "keyword", or "hybrid" | Force a specific search mode. Must be compatible with index\_method. |
| fusion\_method  | "rrf" or "max"                   | Override the fusion method.                                          |

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      retrieval_type: "hybrid",
      fusion_method: "rrf",
    },
  },
});
```

Note

Requesting a `retrieval_type` that is not compatible with the instance `index_method` returns an error.

## Scoring details

When hybrid search is active, each chunk includes a `scoring_details` object:

| Field            | Type   | Description                                 |
| ---------------- | ------ | ------------------------------------------- |
| vector\_score    | number | Vector similarity score (0 to 1).           |
| keyword\_score   | number | Raw BM25 keyword score.                     |
| vector\_rank     | number | Rank position in the vector result set.     |
| keyword\_rank    | number | Rank position in the keyword result set.    |
| fusion\_method   | string | Fusion method used (rrf or max).            |
| reranking\_score | number | Score from the reranking model, if enabled. |

## Limits

Instances with keyword search enabled support up to 500,000 files per instance on the Workers Paid tier, compared to 1,000,000 for vector-only instances. Refer to [Limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/) for the full list of limits.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/#page","headline":"Hybrid search · Cloudflare AI Search docs","description":"Combine vector and keyword search in AI Search for broader, more accurate retrieval results.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/hybrid-search/","name":"Hybrid search"}}]}
```

---

---
title: Keyword search
description: Enable BM25 keyword search in AI Search to match documents containing exact query terms.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Keyword search

Enable keyword search to match chunks that contain your query terms exactly. For an overview of search modes, refer to [Search modes](https://developers.cloudflare.com/ai-search/concepts/search-modes/).

## Enable keyword search

Set `index_method.keyword` to `true` when creating or updating an instance. You can use keyword search on its own or alongside vector search for [hybrid search](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/).

| Field   | Type    | Default | Description                      |
| ------- | ------- | ------- | -------------------------------- |
| vector  | boolean | true    | Enable vector (semantic) search. |
| keyword | boolean | false   | Enable keyword (BM25) search.    |

At least one of `vector` or `keyword` must be `true`. Changing `index_method` triggers a full reindex of your content.

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "my-instance",
  index_method: {
    vector: false,
    keyword: true,
  },
});
```

## Keyword tokenizer

The `keyword_tokenizer` field (inside `indexing_options`) controls how text is split into tokens. Changing this triggers a full reindex.

| Value   | Default | Description                                                                       |
| ------- | ------- | --------------------------------------------------------------------------------- |
| porter  | Yes     | Applies Porter stemming. "running" matches "run." Best for natural language.      |
| trigram | No      | Overlapping 3-character windows. "config" matches "configuration." Best for code. |

## Keyword match mode

The `keyword_match_mode` field (inside `retrieval_options`) controls how multiple query terms are combined.

| Value | Default | Description                                                   |
| ----- | ------- | ------------------------------------------------------------- |
| and   | Yes     | All query terms must appear. Higher precision, fewer results. |
| or    | No      | Any query term can match. Higher recall, more results.        |

You can override `keyword_match_mode` per request:

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      keyword_match_mode: "or",
    },
  },
});
```

## Limits

Instances with keyword search enabled support up to 500,000 files per instance on the Workers Paid tier, compared to 1,000,000 for vector-only instances. Refer to [Limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/) for the full list of limits.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/#page","headline":"Keyword search · Cloudflare AI Search docs","description":"Enable BM25 keyword search in AI Search to match documents containing exact query terms.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/keyword-search/","name":"Keyword search"}}]}
```

---

---
title: Metadata attributes
description: Define built-in and custom metadata attributes on indexed documents.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Metadata attributes

Use metadata attributes to organize your indexed documents and provide context to guide AI responses. This page covers built-in metadata attributes and custom metadata schemas. To filter search results by these attributes at query time, refer to [Filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).

## Built-in metadata attributes

AI Search automatically extracts the following metadata attributes from your indexed documents:

| Attribute | Description                                                                                         | Example                                                                 |
| --------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| filename  | The name of the file.                                                                               | guide.pdf or docs/getting-started/guide.pdf                             |
| folder    | The folder or prefix to the object.                                                                 | For docs/getting-started/guide.pdf, the folder is docs/getting-started/ |
| timestamp | Unix timestamp (milliseconds) when the object was last modified. Comparisons round down to seconds. | 1735689600000 (2025-01-01 00:00:00 UTC)                                 |

## Custom metadata attributes

Custom metadata allows you to define additional fields for filtering search results. You can attach structured metadata to documents and filter queries by attributes such as category, version, or any custom field.

### Supported data types

| Type     | Description                        | Example values               |
| -------- | ---------------------------------- | ---------------------------- |
| text     | String values (max 500 characters) | "documentation", "blog-post" |
| number   | Numeric values (parsed as float)   | 2.5, 100, \-3.14             |
| boolean  | Boolean values                     | true, false, 1, 0, yes, no   |
| datetime | Date and time values               | "2026-01-15T00:00:00Z"       |

### Define a schema

Before custom metadata can be extracted, define a schema in your AI Search configuration using the `custom_metadata` field. The schema specifies which fields to extract and their data types.

**TypeScript**

```ts
custom_metadata: [
  { field_name: "category", data_type: "text" },
  { field_name: "version", data_type: "number" },
  { field_name: "is_public", data_type: "boolean" },
];
```

**Schema constraints:**

* Maximum of 5 custom metadata fields per AI Search instance
* Field names are case-insensitive and stored as lowercase
* Field names cannot use reserved names: `timestamp`, `folder`, `filename`
* Text values are truncated to 500 characters
* Changing the schema triggers a full re-index of all documents

### Add custom metadata attributes to documents

How you attach custom metadata attributes depends on your data source:

* **R2 bucket**: Set metadata using S3-compatible custom headers (`x-amz-meta-*`). Refer to [R2 custom metadata](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/#custom-metadata) for examples.
* **Website**: Add `<meta>` tags to your HTML pages. Refer to [Website custom metadata](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#custom-metadata) for details.
* **Built-in storage**: Attach metadata when uploading files through the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/#upload-with-metadata).

## Re-indexing behavior

When you modify the `custom_metadata` schema:

1. New fields are added to the search index.
2. Removed fields are deleted from the search index.
3. A full re-index is triggered for all documents.
4. Existing vectors are updated with the new metadata structure.

## Limitations

| Constraint                | Limit                       |
| ------------------------- | --------------------------- |
| Maximum custom fields     | 5 per AI Search instance    |
| Maximum text value length | 500 characters              |
| Reserved field names      | timestamp, folder, filename |
| Field name matching       | Case-insensitive            |

If file metadata exceeds size limits, the metadata is replaced with an error indicator:

```json
{
  "file": { "error": "metadata is too large" }
}
```

To avoid this, keep individual metadata values concise.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/#page","headline":"Metadata attributes · Cloudflare AI Search docs","description":"Define built-in and custom metadata attributes on indexed documents.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-17","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/metadata/","name":"Metadata attributes"}}]}
```

---

---
title: Path filtering
description: Control which files or URLs AI Search indexes by defining include and exclude path patterns.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Path filtering

Path filtering allows you to control which files or URLs are indexed by defining include and exclude patterns. Use this to limit indexing to specific content or to skip files you do not want searchable.

Path filtering works with both [website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) and [R2](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/) data sources.

## Configuration

You can configure path filters when creating or editing an AI Search instance. In the dashboard, open **Path Filters** and add your include or exclude rules. You can also update path filters at any time from the **Settings** page of your instance.

When using the REST API, specify `include_items` and `exclude_items` in the `source_params` of your configuration:

| Parameter      | Type       | Limit               | Description                                              |
| -------------- | ---------- | ------------------- | -------------------------------------------------------- |
| include\_items | string\[\] | Maximum 10 patterns | Only index items matching at least one of these patterns |
| exclude\_items | string\[\] | Maximum 10 patterns | Skip items matching any of these patterns                |

Both parameters are optional. If neither is specified, all items from the data source are indexed.

## Filtering behavior

### Wildcard rules

Exclude rules take precedence over include rules. Filtering is applied in this order:

1. **Exclude check**: If the item matches any exclude pattern, it is skipped.
2. **Include check**: If include patterns are defined and the item does not match any of them, it is skipped.
3. **Index**: The item proceeds to indexing.

| Scenario                    | Behavior                                                                               |
| --------------------------- | -------------------------------------------------------------------------------------- |
| No rules defined            | All items are indexed                                                                  |
| Only exclude\_items defined | All items except those matching exclude patterns are indexed                           |
| Only include\_items defined | Only items matching at least one include pattern are indexed                           |
| Both defined                | Exclude patterns are checked first, then remaining items must match an include pattern |

### Pattern syntax

Patterns use a case-sensitive wildcard syntax based on [micromatch ↗](https://github.com/micromatch/micromatch):

| Wildcard | Meaning                                              |
| -------- | ---------------------------------------------------- |
| \*       | Matches any characters except path separators (/)    |
| \*\*     | Matches any characters including path separators (/) |

Patterns can contain:

* Letters, numbers, and underscores (`a-z`, `A-Z`, `0-9`, `_`)
* Hyphens (`-`) and dots (`.`)
* Path separators (`/`)
* URL characters (`?`, `:`, `=`, `&`, `%`)
* Wildcards (`*`, `**`)

### Indexing job status

Items skipped by filtering rules are recorded in job logs with the reason:

* Exclude match: `Skipped by rule: {pattern}`
* No include match: `Skipped by Include Rules`

You can view these in the Jobs tab of your AI Search instance to verify your filters are working as expected.

### Important notes

* **Case sensitivity:** Pattern matching is case-sensitive. `/Blog/*` does not match `/blog/post.html`.
* **Full path matching:** Patterns match the entire path or URL. Use `**` at the beginning for partial matching. For example, `docs/*` matches `docs/file.pdf` but not `site/docs/file.pdf`, while `**/docs/*` matches both.
* **Single `*` does not cross directories:** Use `**` to match across path separators. For example, `docs/*` matches `docs/file.pdf` but not `docs/sub/file.pdf`, while `docs/**` matches both.
* **Trailing slashes matter:** URLs are matched as-is without normalization. `/blog/` does not match `/blog`.

## Examples

### R2 data source

| Use case                        | Pattern                                         | Indexed                            | Skipped                           |
| ------------------------------- | ----------------------------------------------- | ---------------------------------- | --------------------------------- |
| Index only PDFs in docs         | Include: /docs/\*\*/\*.pdf                      | /docs/guide.pdf, /docs/api/ref.pdf | /docs/guide.md, /images/logo.png  |
| Exclude temp and backup files   | Exclude: \*\*/\*.tmp, \*\*/\*.bak               | /docs/guide.md                     | /data/cache.tmp, /old.bak         |
| Exclude temp and backup folders | Exclude: /temp/\*\*, /backup/\*\*               | /docs/guide.md                     | /temp/file.txt, /backup/data.json |
| Index docs but exclude drafts   | Include: /docs/\*\*, Exclude: /docs/drafts/\*\* | /docs/guide.md                     | /docs/drafts/wip.md               |
| Scope an instance to one tenant | Include: /customers/acme/\*\*                   | /customers/acme/report.pdf         | /customers/globex/report.pdf      |

To give each tenant an isolated instance backed by a single shared bucket, refer to [Multitenancy](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/#r2).

### Website data source

| Use case                      | Pattern                                                 | Indexed                                            | Skipped                                        |
| ----------------------------- | ------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------- |
| Index only blog pages         | Include: \*\*/blog/\*\*                                 | example.com/blog/post, example.com/en/blog/article | example.com/about                              |
| Exclude admin pages           | Exclude: \*\*/admin/\*\*                                | example.com/blog/post                              | example.com/admin/settings                     |
| Exclude login pages           | Exclude: \*\*/login\*                                   | example.com/blog/post                              | example.com/login, example.com/auth/login-form |
| Index docs but exclude drafts | Include: \*\*/docs/\*\*, Exclude: \*\*/docs/drafts/\*\* | example.com/docs/guide                             | example.com/docs/drafts/wip                    |

### API format

When using the API, specify patterns in `source_params`:

```json
{
  "source_params": {
    "include_items": ["<PATTERN_1>", "<PATTERN_2>"],
    "exclude_items": ["<PATTERN_1>", "<PATTERN_2>"]
  }
}
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/#page","headline":"Path filtering · Cloudflare AI Search docs","description":"Control which files or URLs AI Search indexes by defining include and exclude path patterns.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/path-filtering/","name":"Path filtering"}}]}
```

---

---
title: Service API token
description: Create a service API token to grant AI Search read access to R2 buckets for indexing.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Service API token

A service API token grants AI Search permission to access [R2](https://developers.cloudflare.com/r2/) buckets in your account. This token is only required if you connect an R2 bucket as a data source. If you use a website or upload files directly through the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/), you do not need a service API token.

## When you need a service API token

You need a service API token when you create an AI Search instance with `type: "r2"`. The token authorizes AI Search to read objects from your R2 bucket for indexing.

## Create via the dashboard or Wrangler

The simplest way to get a service API token is to create an R2-backed AI Search instance through the [dashboard](https://developers.cloudflare.com/ai-search/get-started/dashboard/) or [Wrangler CLI](https://developers.cloudflare.com/ai-search/get-started/wrangler/) at least once. Cloudflare creates and registers a service token for you automatically during the setup flow.

Once created, the token is saved to your account and reused across all AI Search instances. You do not need to create a new token for each instance.

## Create via the API

If you need to create a service API token programmatically, follow these steps.

### 1\. Create an API token with token creation permissions

You need an [API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with permission to create other tokens.

1. In the Cloudflare dashboard, go to **My Profile** \> **API Tokens**.  
[ Go to **API Tokens** ](https://dash.cloudflare.com/profile/api-tokens)
2. Select **Create Token**.
3. Select **Create Custom Token**.
4. Enter a **Token name**, for example `Token Creator`.
5. Under **Permissions**, select **User** \> **API Tokens** \> **Edit**.
6. Select **Continue to summary**, then select **Create Token**.
7. Copy and save the token value. This is your `CREATOR_TOKEN`.

### 2\. Create the service token

Use the [Create token API](https://developers.cloudflare.com/api/resources/user/subresources/tokens/methods/create/) to create a service token with the AI Search Index Engine permission. Replace `<CREATOR_TOKEN>` with the token from step 1 and `<ACCOUNT_ID>` with your [account ID](https://developers.cloudflare.com/fundamentals/account/find-account-and-zone-ids/).

```bash
curl -X POST "https://api.cloudflare.com/client/v4/user/tokens" \
  -H "Authorization: Bearer <CREATOR_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "AI Search Service API Token",
    "policies": [
      {
        "effect": "allow",
        "resources": {
          "com.cloudflare.api.account.<ACCOUNT_ID>": "*"
        },
        "permission_groups": [
          { "id": "9e9b428a0bcd46fd80e580b46a69963c" }
        ]
      }
    ]
  }'
```

Save the `id` and `value` from the response:

```json
{
  "result": {
    "id": "<CF_API_ID>",
    "name": "AI Search Service API Token",
    "status": "active",
    "value": "<CF_API_KEY>"
  },
  "success": true
}
```

### 3\. Register the token with AI Search

Use the [AI Search tokens API](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/tokens/methods/create/) to register the service token. Replace `<API_TOKEN>` with an API token that has AI Search Edit permissions.

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/tokens" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "cf_api_id": "<CF_API_ID>",
    "cf_api_key": "<CF_API_KEY>",
    "name": "AI Search Service Token"
  }'
```

Save the `id` from the response. This is your `token_id` to pass when creating R2-backed instances:

```json
{
  "success": true,
  "result": {
    "id": "<TOKEN_ID>",
    "name": "AI Search Service Token",
    "cf_api_id": "<CF_API_ID>",
    "created_at": "2025-12-25 01:52:28",
    "enabled": true
  }
}
```

### 4\. Use the token when creating an instance

Pass the `token_id` when creating an R2-backed instance:

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
    "id": "my-r2-docs",
    "type": "r2",
    "source": "<R2_BUCKET_NAME>",
    "token_id": "<TOKEN_ID>"
  }'
```

## Manage your token

Once registered, the service API token is stored securely and reused across all AI Search instances in your account.

Warning

Do not delete your service API token. If you revoke or delete the token, any R2-backed AI Search instances will lose access to their data source and stop indexing.

### Rotate your token

To create a new service API token from the dashboard:

1. Go to an existing AI Search instance in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select **Settings**.
3. Under **General**, find **Service API Token** and select the edit icon.
4. Select **Create a new token**.
5. Select **Save**.

To create a new token via the API, follow [steps 1 through 3](#1-create-an-api-token-with-token-creation-permissions) above.

### View registered tokens

List the service API tokens registered with AI Search in your account:

```bash
curl https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/tokens \
  -H "Authorization: Bearer <API_TOKEN>"
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/#page","headline":"Service API token · Cloudflare AI Search docs","description":"Create a service API token to grant AI Search read access to R2 buckets for indexing.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/service-api-token/","name":"Service API token"}}]}
```

---

---
title: Syncing
description: Understand how AI Search automatically syncs and indexes content from connected data sources.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Syncing

AI Search automatically indexes your content for search. How indexing works depends on your data source.

## External data sources

For instances connected to a [website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) or [R2 bucket](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/), AI Search creates jobs to sync your data source. Jobs run automatically on a schedule, every 6 hours by default, and process new, modified, or deleted files to keep your search index up to date.

You can view job status and history in the **Jobs** tab in the dashboard or using the [Instances API](https://developers.cloudflare.com/ai-search/api/instances/rest-api/).

### Sync interval

By default, AI Search runs a sync job every 6 hours. To change how often scheduled syncs run, use the **Sync interval** setting in the dashboard, or set the `sync_interval` field when you [create](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/#create) or [update](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/#update) an instance through the Workers binding or [REST API](https://developers.cloudflare.com/ai-search/api/instances/rest-api/).

The interval can be 1, 2, 4, 6, 12, or 24 hours. In the API, `sync_interval` is specified in seconds, so the allowed values are `3600` (1 hour), `7200` (2 hours), `14400` (4 hours), `21600` (6 hours, the default), `43200` (12 hours), and `86400` (24 hours).

### Trigger syncs from automated pipelines

Sync jobs normally run on a schedule, but you can also start one programmatically whenever your source content changes. This is useful for connecting AI Search to a CMS or a content pipeline: when a publish event or a build step completes, have it trigger a sync so the index reflects the change without waiting for the next scheduled run.

Trigger a sync job with the Wrangler CLI, for example from a CI/CD step or deploy hook:

```sh
npx wrangler ai-search jobs create <INSTANCE_NAME>
```

Or call the [Create job REST API](https://developers.cloudflare.com/ai-search/api/instances/rest-api/#jobs) from a CMS webhook or a Worker. Sync jobs can be triggered at most once every 30 seconds.

## Built-in storage

Files uploaded to [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) are indexed immediately. There are no sync jobs. Each file is processed individually as it is uploaded.

## Controls

| Action               | Description                                                                                                 |
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
| Trigger sync         | Manually start a sync job to scan your external data source for changes. Can be triggered every 30 seconds. |
| Cancel job           | Cancel a running sync job.                                                                                  |
| Pause indexing       | Temporarily stop all scheduled sync jobs.                                                                   |
| Resume indexing      | Resume scheduled sync jobs, including jobs paused automatically after inactivity.                           |
| Sync individual file | Re-index a specific file.                                                                                   |

You can perform these actions from the dashboard, the [REST API](https://developers.cloudflare.com/ai-search/api/instances/rest-api/), or the [Workers binding](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/).

## Performance

The total time to index depends on the number and type of files. Factors that affect performance include:

* Total number of files and their sizes
* File formats (for example, images take longer than plain text)
* Latency of Workers AI models used for embedding and image processing

## Automatic pausing for inactive instances

If an instance receives no search request for 31 days, AI Search automatically pauses its scheduled sync jobs. This applies only to [external data sources](#external-data-sources) (website or R2), since [built-in storage](#built-in-storage) has no sync jobs. This avoids unnecessary requests to your data source to rescan and sync your instance when it is not being used.

A paused instance stays fully searchable, but source changes are not picked up while sync jobs are paused. After the instance receives search or chat traffic again, AI Search automatically resumes scheduled sync jobs during its activity checks. You can also resume manually with the **Resume indexing** control. Refer to [Controls](#controls).

## Best practices

To ensure smooth and reliable indexing:

* Make sure your files are within the [size limit](https://developers.cloudflare.com/ai-search/configuration/data-source/#file-limits) and in a [supported format](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types) to avoid being skipped.
* For R2-backed instances, keep your [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) valid to prevent indexing failures.
* Regularly clean up outdated or unnecessary content to stay within [instance limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/#page","headline":"Syncing · Cloudflare AI Search docs","description":"Understand how AI Search automatically syncs and indexes content from connected data sources.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/syncing/","name":"Syncing"}}]}
```

---

---
title: Vector search
description: Configure vector search in AI Search to find semantically similar content using embeddings.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Vector search

Vector search converts your query into a vector embedding and finds chunks with similar meaning. It is enabled by default on all AI Search instances. For an overview of search modes, refer to [Search modes](https://developers.cloudflare.com/ai-search/concepts/search-modes/).

## Built-in vector index

AI Search instances include a built-in vector index powered by [Vectorize](https://developers.cloudflare.com/vectorize/). The vector index stores embeddings generated from your content and is created and maintained automatically. You do not need to create or manage a Vectorize index yourself.

## Embedding model

The [embedding model](https://developers.cloudflare.com/ai-search/configuration/models/) determines the vector dimensions for the vector index. The embedding model is set when creating an instance and cannot be changed after creation.

## Disable vector search

Vector search is the default index method for all instances. To switch to [keyword search](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) only, set `index_method.vector` to `false`. At least one of `vector` or `keyword` must be `true`.

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "my-instance",
  index_method: {
    vector: false,
    keyword: true,
  },
});
```

## Per-request overrides

You can force vector-only search on a per-request basis using `ai_search_options.retrieval.retrieval_type`, even if keyword search is also enabled on the instance.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      retrieval_type: "vector",
    },
  },
});
```

## Scoring details

When using vector search, each chunk includes a `scoring_details` object:

| Field         | Type   | Description                       |
| ------------- | ------ | --------------------------------- |
| vector\_score | number | Vector similarity score (0 to 1). |
| vector\_rank  | number | Rank position in the result set.  |

## Limits

For vector index limits, refer to [Limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/#page","headline":"Vector search · Cloudflare AI Search docs","description":"Configure vector search in AI Search to find semantically similar content using embeddings.","url":"https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-19","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/indexing/","name":"Indexing"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/indexing/vector-search/","name":"Vector search"}}]}
```

---

---
title: Models
description: Configure which AI models AI Search uses for embedding, generation, reranking, and query rewriting.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Models

AI Search uses models at multiple stages. You can configure which models are used, or let AI Search automatically select a smart default for you.

## Models usage

AI Search leverages Workers AI models in the following stages:

* Image to markdown conversion (if images are in data source): Converts image content to Markdown using object detection and captioning models.
* Embedding: Transforms your documents and queries into vector representations for semantic search.
* Query rewriting (optional): Reformulates the user’s query to improve retrieval accuracy.
* Reranking (optional): Reorders retrieved results by semantic relevance using a cross-encoder model.
* Generation: Produces the final response from retrieved context.

## Model providers

All AI Search instances support models from [Workers AI](https://developers.cloudflare.com/workers-ai). You can use other providers (such as OpenAI or Anthropic) in AI Search by adding their API keys to an [AI Gateway](https://developers.cloudflare.com/ai-gateway) and connecting that gateway to your AI Search.

To use AI Search with other model providers:

1. Add provider keys to [AI Gateway](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/).
2. Connect the gateway to AI Search.

  * When creating a new AI Search, select the AI Gateway with your provider keys.
  * For an existing AI Search, go to **Settings** and switch to a gateway that has your keys under **Resources**.
3. Select models

  * Embedding model: Only available to be changed when creating a new AI Search.
  * Generation model: Can be selected when creating a new AI Search and can be changed at any time in **Settings**.

AI Search supports a subset of models that have been selected to provide the best experience. Refer to the list of [supported models](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).

### Smart default

If you choose **Smart Default** in your model selection, then AI Search will select a Cloudflare recommended model and will update it automatically for you over time. You can switch to explicit model configuration at any time by visiting **Settings**.

### Per-request generation model override

While the generation model can be set globally at the AI Search instance level, you can also override it on a per-request basis in the [AI Search API](https://developers.cloudflare.com/ai-search/api/search/rest-api/#chat-completions). This is useful when you need dynamic selection of generation models based on context or user preferences.

## Model deprecation

AI Search may deprecate support for a given model in order to provide support for better-performing models with improved capabilities. When a model is being deprecated, we announce the change and provide an end-of-life date after which the model will no longer be accessible. Applications that depend on AI Search may therefore require occasional updates to continue working reliably.

### Model lifecycle

AI Search models follow a defined lifecycle to ensure stability and predictable deprecation:

1. **Production:** The model is actively supported and recommended for use. It is included in Smart Defaults and receives ongoing updates and maintenance.
2. **Announcement & Transition:** The model remains available but has been marked for deprecation. An end-of-life date is communicated through documentation, release notes, and other official channels. During this phase, users are encouraged to migrate to the recommended replacement model.
3. **Automatic Upgrade (if applicable):** If you have selected the Smart Default option, AI Search will automatically upgrade requests to a recommended replacement.
4. **End of life:** The model is no longer available. Any requests to the retired model return a clear error message, and the model is removed from documentation and Smart Defaults.

Learn more about models and their lifecycle status in [supported models](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).

### Best practices

* Regularly check the [release note](https://developers.cloudflare.com/ai-search/platform/release-note/) for updates.
* Plan migration efforts according to the communicated end-of-life date.
* Migrate and test the recommended replacement models before the end-of-life date.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/models/#page","headline":"Models · Cloudflare AI Search docs","description":"Configure which AI models AI Search uses for embedding, generation, reranking, and query rewriting.","url":"https://developers.cloudflare.com/ai-search/configuration/models/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/models/","name":"Models"}}]}
```

---

---
title: AI Gateway
description: Observe and control the AI models your AI Search instance uses through the connected AI Gateway.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# AI Gateway

Every AI Search instance is connected to a Cloudflare [AI Gateway](https://developers.cloudflare.com/ai-gateway/). The model calls that AI Search makes for embedding, query rewriting, reranking, and response generation run through this gateway. By configuring the connected gateway, you can observe and control those model calls.

To choose or change which gateway your instance uses, see [Models](https://developers.cloudflare.com/ai-search/configuration/models/).

## Observe your model calls

AI Gateway records the model requests that run through it, so you can see what your instance is doing.

* **[Analytics](https://developers.cloudflare.com/ai-gateway/observability/analytics/):** Track the number of requests, tokens used, cost, latency, and errors across your model calls.
* **[Logs](https://developers.cloudflare.com/ai-gateway/observability/logging/):** Inspect individual requests and responses, including the effective [system prompt](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/), rewritten queries, and generated answers.

## Use models from other providers

By default, AI Search uses [Workers AI](https://developers.cloudflare.com/workers-ai/) models. To use models from other providers, such as OpenAI or Anthropic, add your provider keys to AI Gateway and select those models in AI Search.

1. Add your provider keys with [Bring Your Own Keys](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/).
2. Connect the gateway and select the models in your AI Search settings. For details, see [Models](https://developers.cloudflare.com/ai-search/configuration/models/).

## Guard against unsafe content

Use AI Gateway [Guardrails](https://developers.cloudflare.com/ai-gateway/features/guardrails/) to screen the prompts and responses that flow through your instance and block content that is unsafe or inappropriate. To detect and handle sensitive information, such as personal or financial data, use [Data Loss Prevention (DLP)](https://developers.cloudflare.com/ai-gateway/features/dlp/).

## Improve resilience

Configure [request retries and model fallbacks](https://developers.cloudflare.com/ai-gateway/configuration/fallbacks/) so that a model call can automatically retry or fall back to another model when a provider returns an error.

## Caching and rate limiting

Some AI Gateway features act on every request that passes through the gateway. Because your AI Search instance shares this gateway for its internal model calls, a few features can interfere with indexing and querying.

Do not turn on [AI Gateway caching](https://developers.cloudflare.com/ai-gateway/features/caching/) for the gateway connected to your AI Search instance. This matters most for embedding requests. AI Search relies on fresh embeddings to build its vector index and to match each query against it, so serving cached embeddings can store or return incorrect vectors and quietly degrade the accuracy of your search results. To cache search results, use AI Search's own [Similarity cache](https://developers.cloudflare.com/ai-search/configuration/retrieval/cache/) instead.

Similarly, avoid setting [rate limiting](https://developers.cloudflare.com/ai-gateway/features/rate-limiting/) on this gateway. Rate limits apply to AI Search's own model calls, including the many embedding requests made while indexing, and can interrupt indexing and querying.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/models/ai-gateway/#page","headline":"AI Gateway · Cloudflare AI Search docs","description":"Observe and control the AI models your AI Search instance uses through the connected AI Gateway.","url":"https://developers.cloudflare.com/ai-search/configuration/models/ai-gateway/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-06","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/models/","name":"Models"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/models/ai-gateway/","name":"AI Gateway"}}]}
```

---

---
title: Supported models
description: View all AI models supported by AI Search, including text generation, embedding, and reranking models.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Supported models

This page lists all models supported by AI Search and their lifecycle status.

Request model support

If you would like to use a model that is not currently supported, reach out to us on [Discord ↗](https://discord.gg/cloudflaredev) to request it.

## Production models

Production models are the actively supported and recommended models that are stable and fully available.

### Text generation

| Provider             | Alias                                       | Context window (tokens) |
| -------------------- | ------------------------------------------- | ----------------------- |
| **Anthropic**        | anthropic/claude-3-7-sonnet                 | 200,000                 |
|                      | anthropic/claude-sonnet-4                   | 200,000                 |
|                      | anthropic/claude-opus-4                     | 200,000                 |
|                      | anthropic/claude-3-5-haiku                  | 200,000                 |
| **Cerebras**         | cerebras/qwen-3-235b-a22b-instruct          | 64,000                  |
|                      | cerebras/qwen-3-235b-a22b-thinking          | 65,000                  |
|                      | cerebras/llama-3.3-70b                      | 65,000                  |
|                      | cerebras/llama-4-maverick-17b-128e-instruct | 8,000                   |
|                      | cerebras/llama-4-scout-17b-16e-instruct     | 8,000                   |
|                      | cerebras/gpt-oss-120b                       | 64,000                  |
| **Google AI Studio** | google-ai-studio/gemini-2.5-flash           | 1,048,576               |
|                      | google-ai-studio/gemini-2.5-pro             | 1,048,576               |
| **Grok (x.ai)**      | grok/grok-4                                 | 256,000                 |
| **Groq**             | groq/llama-3.3-70b-versatile                | 131,072                 |
|                      | groq/llama-3.1-8b-instant                   | 131,072                 |
| **OpenAI**           | openai/gpt-5                                | 400,000                 |
|                      | openai/gpt-5-mini                           | 400,000                 |
|                      | openai/gpt-5-nano                           | 400,000                 |
| **Workers AI**       | @cf/meta/llama-3.3-70b-instruct-fp8-fast    | 24,000                  |
|                      | @cf/meta/llama-3.1-8b-instruct-fast         | 60,000                  |
|                      | @cf/meta/llama-3.1-8b-instruct-fp8          | 32,000                  |
|                      | @cf/meta/llama-4-scout-17b-16e-instruct     | 131,000                 |
|                      | @cf/zai-org/glm-4.7-flash                   | 131,072                 |
|                      | @cf/qwen/qwen3-30b-a3b-fp8                  | 32,000                  |

### Embedding

| Provider             | Alias                                 | Vector dims | Input tokens | Metric |
| -------------------- | ------------------------------------- | ----------- | ------------ | ------ |
| **Google AI Studio** | google-ai-studio/gemini-embedding-001 | 1,536       | 2048         | cosine |
| **OpenAI**           | openai/text-embedding-3-small         | 1,536       | 8192         | cosine |
|                      | openai/text-embedding-3-large         | 1,536       | 8192         | cosine |
| **Workers AI**       | @cf/baai/bge-m3                       | 1,024       | 512          | cosine |
|                      | @cf/baai/bge-large-en-v1.5            | 1,024       | 512          | cosine |
|                      | @cf/qwen/qwen3-embedding-0.6b         | 1,024       | 8,192        | cosine |
|                      | @cf/google/embeddinggemma-300m        | 768         | 512          | cosine |

### Reranking

| Provider       | Alias                      | Input tokens |
| -------------- | -------------------------- | ------------ |
| **Workers AI** | @cf/baai/bge-reranker-base | 512          |

## Transition models

There are currently no models marked for end-of-life.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/models/supported-models/#page","headline":"Supported models · Cloudflare AI Search docs","description":"View all AI models supported by AI Search, including text generation, embedding, and reranking models.","url":"https://developers.cloudflare.com/ai-search/configuration/models/supported-models/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-17","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/models/","name":"Models"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/models/supported-models/","name":"Supported models"}}]}
```

---

---
title: Relevance boosting
description: Bias AI Search results toward documents with specific metadata using relevance boosting.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Relevance boosting

Boosting lets you bias search results toward documents with specific metadata characteristics. For example, you can promote recent documents, surface higher-priority pages, or deprioritize drafts. Boosting re-ranks results without replacing semantic relevance.

## How it works

Boosting applies after the initial retrieval step and before [reranking](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/) (if enabled):

1. **Search**: AI Search retrieves up to 50 candidate chunks using vector search, keyword search, or both.
2. **Boost**: Each candidate is re-scored using the metadata fields you specify in `boost_by`. The boost is additive to the original retrieval score.
3. **Rerank**: If reranking is enabled, the boosted results are reranked by a reranking model.
4. **Return**: The top `max_num_results` are returned.

Boosting can change the order of results within the candidate set, but cannot promote a chunk that the initial search step did not retrieve.

## Supported fields

You can boost by the built-in `timestamp` field or by any field defined in your [custom metadata schema](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/#define-a-schema).

| Field type | Supported directions           |
| ---------- | ------------------------------ |
| datetime   | asc, desc, exists, not\_exists |
| number     | asc, desc, exists, not\_exists |
| text       | exists, not\_exists only       |
| boolean    | exists, not\_exists only       |

### Directions

The direction controls how the field value affects the ranking of each result:

| Direction   | Effect                                                       |
| ----------- | ------------------------------------------------------------ |
| desc        | Higher field values score higher (for example, most recent). |
| asc         | Lower field values score higher (for example, lowest cost).  |
| exists      | Documents that have the field score higher.                  |
| not\_exists | Documents that do not have the field score higher.           |

If you omit `direction`, AI Search applies a default based on the field type:

| Field type                  | Default direction |
| --------------------------- | ----------------- |
| number, datetime, timestamp | asc               |
| text, boolean               | exists            |

Using `asc` or `desc` on a `text` or `boolean` field returns an error.

## Configuration

Specify `boost_by` as an array of up to 3 objects when creating or updating an instance. Each object must reference a unique field.

| Field     | Type   | Required | Description                                                                 |
| --------- | ------ | -------- | --------------------------------------------------------------------------- |
| field     | string | Yes      | Metadata field name or timestamp. Must match your schema. Case-insensitive. |
| direction | string | No       | One of asc, desc, exists, not\_exists. Defaults by type.                    |

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "my-instance",
  retrieval_options: {
    boost_by: [
      { field: "timestamp", direction: "desc" },
      { field: "priority", direction: "desc" },
    ],
  },
});
```

To remove boosting, set `boost_by` to an empty array when updating the instance.

## Per-request overrides

You can override `boost_by` on individual requests using `ai_search_options.retrieval`. Per-request values fully replace the instance-level default.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      boost_by: [{ field: "timestamp", direction: "desc" }],
    },
  },
});
```

To disable boosting for a single request, pass an empty array:

**TypeScript**

```ts
const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      boost_by: [],
    },
  },
});
```

## Common patterns

Here are some common ways to use relevance boosting:

| Pattern                          | Configuration                                                                                   |
| -------------------------------- | ----------------------------------------------------------------------------------------------- |
| Prioritize recent documents      | \[{ "field": "timestamp", "direction": "desc" }\]                                               |
| Promote by custom priority       | \[{ "field": "priority", "direction": "desc" }\]                                                |
| Boost lower-cost options         | \[{ "field": "cost", "direction": "asc" }\]                                                     |
| Promote documents with an author | \[{ "field": "author", "direction": "exists" }\]                                                |
| Suppress drafts                  | \[{ "field": "draft", "direction": "not\_exists" }\]                                            |
| Combine recency and priority     | \[{ "field": "timestamp", "direction": "desc" }, { "field": "priority", "direction": "desc" }\] |

## Limitations

* Maximum of 3 boost fields per request.
* Field names must match a field in your custom metadata schema or the built-in `timestamp` field.
* `text` and `boolean` fields only support `exists` and `not_exists` directions.
* Boost fields within a single request must be unique.
* Boosting re-ranks the candidate set from the initial search. It cannot surface documents that were not retrieved.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/boosting/#page","headline":"Relevance boosting · Cloudflare AI Search docs","description":"Bias AI Search results toward documents with specific metadata using relevance boosting.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/boosting/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/boosting/","name":"Relevance boosting"}}]}
```

---

---
title: Similarity cache
description: Speed up AI Search responses by caching and reusing answers for semantically similar queries.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Similarity cache

Similarity-based caching in AI Search lets you serve responses from Cloudflare's cache for queries that are similar to previous requests, rather than creating new, unique responses for every request. This speeds up response times and cuts costs by reusing answers for questions that are close in meaning.

## How it works

Unlike with basic caching, which creates a new response with every request, this is what happens when a request is received using similarity-based caching:

1. AI Search checks if a _similar_ prompt (based on your chosen threshold) has been answered before.
2. If a match is found, it returns the cached response instantly.
3. If no match is found, it generates a new response and caches it.

To see if a response came from the cache, check the `cf-aig-cache-status` header: `HIT` for cached and `MISS` for new.

## What to consider when using similarity cache

Consider these behaviors when using similarity caching:

* **Volatile Cache**: If two similar requests hit at the same time, the first might not cache in time for the second to use it, resulting in a `MISS`.
* **Configurable duration**: Cached responses expire based on the instance's `cache_ttl` setting. The default is 48 hours.
* **Data Dependency**: Cached responses are tied to specific document chunks. If those chunks change or get deleted, the cache clears to keep answers fresh.

## How similarity matching works

AI Search's similarity cache uses **MinHash and Locality-Sensitive Hashing (LSH)** to find and reuse responses for prompts that are worded similarly.

Here's how it works when a new prompt comes in:

1. The prompt is split into small overlapping chunks of words (called shingles), like "what's the" or "the weather."
2. These shingles are turned into a "fingerprint" using MinHash. The more overlap two prompts have, the more similar their fingerprints will be.
3. Fingerprints are placed into LSH buckets, which help AI Search quickly find similar prompts without comparing every single one.
4. If a past prompt in the same bucket is similar enough (based on your configured threshold), AI Search reuses its cached response.

## Choose a threshold

The similarity threshold decides how close two prompts need to be to reuse a cached response. You can set the threshold at the instance level or override it per request.

| Threshold | API value               | Description                 | Example match                                                                   |
| --------- | ----------------------- | --------------------------- | ------------------------------------------------------------------------------- |
| Exact     | super\_strict\_match    | Near-identical matches only | "What's the weather like today?" matches with "What is the weather like today?" |
| Strong    | close\_enough (default) | High semantic similarity    | "What's the weather like today?" matches with "How's the weather today?"        |
| Broad     | flexible\_friend        | Moderate match, more hits   | "What's the weather like today?" matches with "Tell me today's weather"         |
| Loose     | anything\_goes          | Low similarity, max reuse   | "What's the weather like today?" matches with "Give me the forecast"            |

## Set cache duration

Set `cache_ttl` when creating or updating an instance to control how long the instance retains cached responses. Allowed values are:

| Duration   | API value |
| ---------- | --------- |
| 10 minutes | 600       |
| 30 minutes | 1800      |
| 1 hour     | 3600      |
| 2 hours    | 7200      |
| 6 hours    | 21600     |
| 12 hours   | 43200     |
| 24 hours   | 86400     |
| 48 hours   | 172800    |
| 72 hours   | 259200    |
| 6 days     | 518400    |

## Purge cached responses

To clear all cached responses for an instance immediately, use the purge cache operation:

```sh
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$INSTANCE_NAME/purge_cache" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

Purging the cache rotates the instance's internal cache key, so new queries do not reuse previous cached responses.

You can also purge cached responses from the instance settings page in the Cloudflare dashboard.

## Per-request cache override

You can override the instance-level cache setting on a per-request basis using the `cache` parameter in `ai_search_options`:

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    cache: {
      enabled: true,
      cache_threshold: "flexible_friend",
    },
  },
});
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/cache/#page","headline":"Similarity cache · Cloudflare AI Search docs","description":"Speed up AI Search responses by caching and reusing answers for semantically similar queries.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/cache/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-24","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/cache/","name":"Similarity cache"}}]}
```

---

---
title: UI snippets
description: Add AI Search to your website using pre-built, customizable web components for search and chat.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# UI snippets

You can add AI Search easily into your website using the [Cloudflare AI Search UI snippet library ↗](https://search.ai.cloudflare.com/), which provides production-ready, customizable web components.

The library is open source at [github.com/cloudflare/ai-search-snippet ↗](https://github.com/cloudflare/ai-search-snippet).

## Available components

The snippet library provides four web components. Each component connects to your AI Search instance using the `api-url` attribute, which should point to your public endpoint URL.

| Component              | Description                                                 |
| ---------------------- | ----------------------------------------------------------- |
| <search-bar-snippet>   | An inline search bar that displays results in a dropdown    |
| <search-modal-snippet> | A search modal that opens with Cmd/Ctrl+K keyboard shortcut |
| <chat-bubble-snippet>  | A floating chat bubble in the corner of the page            |
| <chat-page-snippet>    | A full-page chat interface with conversation history        |

For advanced styling and configuration, visit [search.ai.cloudflare.com ↗](https://search.ai.cloudflare.com/).

## Prerequisites

UI snippets connect to your AI Search instance through a public endpoint. You need to enable this endpoint before using the snippets.

1. Go to **AI Search** in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your AI Search instance.
3. Go to **Settings** \> **Public Endpoint**.
4. Turn on **Enable Public Endpoint**.
5. Copy the public endpoint URL. You will use this as the `api-url` attribute in your snippets.

## Use with HTML

1. Add the script tag to your HTML file (for example, `index.html`). Replace `<INSTANCE_ID>` with your AI Search instance's public endpoint ID, which you can find in your AI Search instance's **Settings** \> **Public Endpoint**.  
```html  
<script  
  type="module"  
  src="https://<INSTANCE_ID>.search.ai.cloudflare.com/assets/v0.0.25/search-snippet.es.js"  
></script>  
```
2. Add a component with your `api-url`.  
```html  
<search-bar-snippet  
  api-url="https://<INSTANCE_ID>.search.ai.cloudflare.com/"  
  placeholder="Search..."  
></search-bar-snippet>  
```
3. Before testing, [configure CORS](#configure-cors-for-local-testing) to allow your local origin. Then open the HTML file in your browser to test.

### Full HTML example

The following example shows a complete HTML page with a search bar. When a user types in the search bar, results appear in a dropdown below.

```html
<!doctype html>
<html>
  <head>
    <script
      type="module"
      src="https://<INSTANCE_ID>.search.ai.cloudflare.com/assets/v0.0.25/search-snippet.es.js"
    ></script>
  </head>
  <body>
    <search-bar-snippet
      api-url="https://<INSTANCE_ID>.search.ai.cloudflare.com/"
      placeholder="Search..."
      max-results="10"
    ></search-bar-snippet>
  </body>
</html>
```

## Use with a framework

* [ React ](#tab-panel-7201)
* [ Vue ](#tab-panel-7202)

1. Open your React project and install the package:  
```bash  
npm install @cloudflare/ai-search-snippet  
```
2. In your component file (for example, `src/App.tsx`), import the package:  
```tsx  
import "@cloudflare/ai-search-snippet";  
```
3. Add a component to your JSX:  
```tsx  
export default function App() {  
  return (  
    <search-bar-snippet  
      api-url="https://<INSTANCE_ID>.search.ai.cloudflare.com/"  
      placeholder="Search..."  
    />  
  );  
}  
```
4. Before testing, [configure CORS](#configure-cors-for-local-testing) to allow your local origin. Then run your development server:  
```bash  
npm run dev  
```

The package includes TypeScript types and works with React, Next.js, and other React frameworks.

1. Open your Vue project and install the package:  
```bash  
npm install @cloudflare/ai-search-snippet  
```
2. In your component file (for example, `src/App.vue`), import the package and add the component:  
```vue  
<template>  
  <search-bar-snippet :api-url="apiUrl" placeholder="Search..." />  
</template>  
<script setup>  
import "@cloudflare/ai-search-snippet";  
const apiUrl = "https://<INSTANCE_ID>.search.ai.cloudflare.com/";  
</script>  
```
3. Before testing, [configure CORS](#configure-cors-for-local-testing) to allow your local origin. Then run your development server:  
```bash  
npm run dev  
```

## Configure a component

Each component accepts attributes that control its behavior. Common attributes include:

| Attribute     | Description                                                 |
| ------------- | ----------------------------------------------------------- |
| api-url       | Required. Your instance's public endpoint URL.              |
| placeholder   | Placeholder text for the input.                             |
| max-results   | Maximum number of results to request.                       |
| theme         | light, dark, or auto (default) to follow the system theme.  |
| hide-branding | Hide the Cloudflare branding.                               |
| translations  | Override the user-facing strings to localize the component. |

The chat components (`<chat-bubble-snippet>` and `<chat-page-snippet>`) also accept `chat-query-rewrite` to rewrite follow-up messages into standalone queries.

For the complete list of attributes and a live editor that generates the HTML, React, or Vue code for you, use the [snippet playground ↗](https://search.ai.cloudflare.com/).

## Customize the appearance

Style the components with CSS custom properties, all prefixed with `--search-snippet-`. Set them on the component or a parent element:

```css
search-bar-snippet {
  --search-snippet-primary-color: #f6821f;
  --search-snippet-border-radius: 12px;
}
```

The [playground ↗](https://search.ai.cloudflare.com/) lists every available variable and previews your changes live.

## Configure CORS for local testing

When testing locally (for example, `http://localhost:3000`), you need to allow your local origin in the public endpoint settings.

1. Go to **AI Search** in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your AI Search instance.
3. Go to **Settings** \> **Public Endpoint**.
4. Under **Authorized hosts**, add your local URL (for example, `http://localhost:3000`) or `*` to allow all origins during testing.
5. Select **Save**.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/#page","headline":"UI snippets · Cloudflare AI Search docs","description":"Add AI Search to your website using pre-built, customizable web components for search and chat.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/embed-search-snippets/","name":"UI snippets"}}]}
```

---

---
title: Filtering
description: Filter AI Search results by metadata attributes at query time.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Filtering

Metadata filtering narrows down search results based on metadata, so only relevant content is retrieved. The filter is applied before retrieval, so you only query the documents that matter.

Filtering uses the metadata attributes extracted during indexing. To define custom attributes or use the built-in metadata attributes, refer to [Metadata attributes](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/).

Note

If you are using the legacy AutoRAG API, refer to [Metadata filter format (legacy)](https://developers.cloudflare.com/ai-search/api/migration/autorag-filter-format/) for the filter syntax.

Here is an example of metadata filtering using the [Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/):

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      filters: {
        folder: "docs/getting-started/",
        timestamp: { $gte: 1735689600 },
      },
    },
  },
});
```

## Filter syntax

Filters are JSON objects where keys are metadata attribute names and values specify the filter condition.

### Supported operators

| Operator | Description                       |
| -------- | --------------------------------- |
| $eq      | Equals                            |
| $ne      | Not equals                        |
| $in      | In (matches any value in array)   |
| $nin     | Not in (excludes values in array) |
| $lt      | Less than                         |
| $lte     | Less than or equal to             |
| $gt      | Greater than                      |
| $gte     | Greater than or equal to          |

### Implicit `$eq`

When you provide a direct value without an operator, it is treated as an equality check:

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": { "folder": "docs/getting-started/" }
    }
  }
}
```

This is equivalent to:

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": { "folder": { "$eq": "docs/getting-started/" } }
    }
  }
}
```

### Range queries

Combine upper and lower bound operators to filter by ranges:

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": { "timestamp": { "$gte": 1735689600, "$lt": 1735900000 } }
    }
  }
}
```

### Multiple conditions (implicit AND)

When you specify multiple keys, all conditions must match:

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": {
        "folder": "docs/getting-started/",
        "timestamp": { "$gte": 1735689600 }
      }
    }
  }
}
```

### `$in` operator

Match any value in an array:

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": { "folder": { "$in": ["docs/guides/", "docs/tutorials/"] } }
    }
  }
}
```

## "Starts with" filter for folders

Use range queries to filter for all files within a folder and its subfolders.

For example, consider this file structure:

* Directorydocs  
  * guide.pdf
  * Directorytutorials  
    * Directorygetting-started  
      * intro.pdf

Using `{ "folder": "docs/" }` only matches files directly in that folder (like `guide.pdf`), not files in subfolders.

To match all files starting with `docs/`, use a range query:

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": { "folder": { "$gte": "docs/", "$lt": "docs0" } }
    }
  }
}
```

This works because:

* `$gte` includes all paths starting with `docs/`
* `$lt` with `docs0` excludes paths that do not start with `docs/` (since `0` comes after `/` in ASCII)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/#page","headline":"Filtering · Cloudflare AI Search docs","description":"Filter AI Search results by metadata attributes at query time.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-17","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/filtering/","name":"Filtering"}}]}
```

---

---
title: Public endpoint settings
description: Expose AI Search instances through public MCP, chat, and search endpoints without authentication.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Public endpoint settings

Configure public endpoints to expose your AI Search instance directly to users without requiring authentication. This enables you to share your AI Search functionality with external users, or to integrate it into public-facing applications.

## Available endpoints

Each AI Search instance can expose three public endpoints:

| Endpoint          | Description                                   |
| ----------------- | --------------------------------------------- |
| /mcp              | Model Context Protocol endpoint for AI agents |
| /chat/completions | OpenAI-compatible chat completion endpoint    |
| /search           | Search endpoint that returns relevant chunks  |

For details on how to use these endpoints, refer to [Public endpoint usage](https://developers.cloudflare.com/ai-search/api/search/public-endpoint/).

## Public URL format

When enabled, public endpoints are accessible at:

```plaintext
https://<hash>.search.ai.cloudflare.com/<endpoint>
```

The `<hash>` is your instance's unique public endpoint identifier.

For example:

* `https://abc123.search.ai.cloudflare.com/mcp`
* `https://abc123.search.ai.cloudflare.com/chat/completions`
* `https://abc123.search.ai.cloudflare.com/search`

## Enabling and disabling public endpoints

You can enable or disable each public endpoint independently:

1. Log in to your Cloudflare account, and go to **AI Search**. [ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your AI Search instance.
3. Go to **Settings** \> **Public Endpoints**.
4. Toggle on **Public Endpoints** to enable the feature, then toggle each individual endpoint on or off as needed.

Each endpoint has its own configuration panel for granular control.

## Rate limiting

Configure rate limits to control usage across all public endpoints:

| Setting             | Description                               | Default  |
| ------------------- | ----------------------------------------- | -------- |
| Requests per period | Maximum number of requests allowed        | 120      |
| Time period         | Time window for the rate limit            | 1 minute |
| Period type         | Rate limiting technique: fixed or sliding | fixed    |

Rate limits apply across all enabled public endpoints for the AI Search instance.

## CORS configuration

Cross-Origin Resource Sharing (CORS) is enabled by default to support browser-based applications.

The default allowed origins depend on your data source type:

* **Website data sources**: The source domain is automatically added as an allowed origin.
* **Other data sources**: All origins (`*`) are allowed by default.

You can customize allowed origins in the **Public Endpoints** settings by adding specific hostnames to the CORS rules.

## Tool description

The **Tool Description** field allows you to customize how your AI Search instance is described to MCP clients. The default description is `Finds exactly what you're looking for`. This description helps AI agents understand what content is available, and when to use your search tool. A good tool description should explain what type of content is indexed, and what kinds of questions it can answer.

For example:

```txt
Search the Acme product documentation for information about
installation, configuration, API references, and troubleshooting
guides. Use this tool when users ask questions about how to set up
or use Acme products.
```

## Security considerations

* Public endpoints do not require authentication.
* Consider enabling rate limiting to prevent abuse.
* Use CORS rules to restrict access to specific domains.
* Monitor usage through your dashboard analytics.

## Related

* [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/) \- Add pre-built search and chat components to your website using your public endpoints.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/#page","headline":"Public endpoint settings · Cloudflare AI Search docs","description":"Expose AI Search instances through public MCP, chat, and search endpoints without authentication.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/public-endpoint/","name":"Public endpoint settings"}}]}
```

---

---
title: Query rewriting
description: Improve AI Search retrieval quality by enabling query rewriting to rephrase user queries.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Query rewriting

Query rewriting is an optional step in the AI Search pipeline that improves retrieval quality for follow-up queries. It applies to both [Search](https://developers.cloudflare.com/ai-search/api/search/workers-binding/#search) and [Chat Completions](https://developers.cloudflare.com/ai-search/api/search/workers-binding/#chatcompletions) requests.

## Why use query rewriting?

The wording of a user's question may not match how your documents are written. Query rewriting helps bridge this gap by:

* Rephrasing informal or vague queries into precise, information-dense terms
* Adding synonyms or related keywords
* Removing filler words or irrelevant details
* Resolving follow-up queries that reference previous messages (for example, "tell me more about that" becomes a specific query based on conversation history)

This leads to more relevant search matches, which improves the accuracy of results and generated responses.

## How it works

Query rewriting requires the `messages` format and does not apply when using the `query` format. The first message is always used as-is. For follow-up queries, AI Search sends the conversation history, the latest user message, and the [query rewrite system prompt](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/) to the configured LLM. The rewritten query is then embedded and used to perform the search.

## Example

**First message:** `What is Cloudflare Workers?`(used as-is, no rewriting)

**Follow-up message:** `How do I deploy one?` **Rewritten query:** `deploy Cloudflare Worker getting started`

The follow-up "How do I deploy one?" is vague on its own. Query rewriting uses the conversation context to understand "one" refers to a Cloudflare Worker. How the query is rewritten depends on your [query rewrite system prompt](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/#query-rewriting-system-prompt).

## Considerations

Enabling query rewriting adds an extra LLM call to the query pipeline, which may increase latency.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/#page","headline":"Query rewriting · Cloudflare AI Search docs","description":"Improve AI Search retrieval quality by enabling query rewriting to rephrase user queries.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/query-rewriting/","name":"Query rewriting"}}]}
```

---

---
title: Reranking
description: Enable reranking in AI Search to reorder retrieved results by semantic relevance.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Reranking

Reranking can help improve the quality of AI Search results by reordering retrieved documents based on semantic relevance to the user's query. It applies a secondary model after retrieval to rerank the top results before they are returned.

## How it works

By default, reranking is **disabled** for all AI Search instances. You can enable it during creation or later from the settings page.

When enabled, AI Search will:

1. Retrieve a set of relevant results from your index, constrained by your `max_num_results` and `score_threshold` parameters.
2. Pass those results through a [reranking model](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).
3. Return the reranked results, which the text generation model can use for answer generation.

Reranking helps improve accuracy, especially for large or noisy datasets where vector similarity alone may not produce the optimal ordering.

## Configuration

When you make a `/search` or `/chat/completions` request using the [Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) or [REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/), you can enable or disable reranking per request and specify the reranking model.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    reranking: {
      enabled: true,
      model: "@cf/baai/bge-reranker-base",
    },
  },
});
```

### Considerations

Adding reranking will include an additional step to the query request. As a result, there may be an increase in the latency of the request.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/#page","headline":"Reranking · Cloudflare AI Search docs","description":"Enable reranking in AI Search to reorder retrieved results by semantic relevance.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/reranking/","name":"Reranking"}}]}
```

---

---
title: Result controls
description: Control AI Search result count and minimum score thresholds for returned results.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Result controls

These settings control how many results are returned and the minimum score required. To filter results by metadata attributes like folder or category, refer to [Filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).

## Match threshold

The `match_threshold` sets the minimum vector similarity score that a chunk must meet to be included in the results. Threshold values range from `0` to `1`. The threshold filters on the vector similarity score, not the fused score returned in the response.

* A higher threshold means stricter filtering, returning only highly similar matches.
* A lower threshold allows broader matches, increasing recall but possibly reducing precision.

## Maximum number of results

The `max_num_results` setting controls the number of top-matching chunks returned. The maximum allowed value is 50.

* Use a higher value if you want to synthesize across multiple documents. However, providing more input to the model can increase latency and cost.
* Use a lower value if you prefer concise answers with minimal context.

## How they work together

1. Your query is embedded using the configured embedding model.
2. The search index is queried. For [hybrid search](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/), vector and keyword results are fused into a single ranked list.
3. Chunks with a vector similarity score below `match_threshold` are filtered out.
4. The filtered results are limited to `max_num_results` and passed into the generation step as context.

If no results meet the threshold, AI Search will not generate a response.

If [reranking](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/) is enabled, a separate `reranking.match_threshold` can be configured to filter chunks by their reranking score.

## Per-request override

These values can be configured at the instance level or overridden per request:

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      match_threshold: 0.5,
      max_num_results: 10,
    },
  },
});
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/result-controls/#page","headline":"Result controls · Cloudflare AI Search docs","description":"Control AI Search result count and minimum score thresholds for returned results.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/result-controls/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-17","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/result-controls/","name":"Result controls"}}]}
```

---

---
title: System prompt
description: Guide AI Search query rewriting and response generation behavior with custom system prompts.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# System prompt

System prompts allow you to guide the behavior of the text-generation models used by AI Search at query time. AI Search supports system prompt configuration in two steps:

* **Query rewriting**: Reformulates the original user query to improve semantic retrieval. A system prompt can guide how the model interprets and rewrites the query.
* **Generation**: Generates the final response from retrieved context. A system prompt can help define how the model should format, filter, or prioritize information when constructing the answer.

## What is a system prompt?

A system prompt is a special instruction sent to a large language model (LLM) that guides how it behaves during inference. The system prompt defines the model's role, context, or rules it should follow.

System prompts are particularly useful for:

* Enforcing specific response formats
* Constraining behavior (for example, it only responds based on the provided content)
* Applying domain-specific tone or terminology
* Encouraging consistent, high-quality output

## System prompt configuration

### Default system prompt

When configuring your AI Search instance, you can provide your own system prompts. If you do not provide a system prompt, AI Search will use the **default system prompt** provided by Cloudflare.

You can view the effective system prompt used for any AI Search's model call through AI Gateway logs, where model inputs and outputs are recorded.

### Configure via API

When you make a `/chat/completions` request using the [Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) or [REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/), you can set the system prompt programmatically.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const response = await instance.chatCompletions({
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "What is Cloudflare?" },
  ],
  model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
});
```

## Generation system prompt

If you are using the Chat Completions endpoint, you can use the system prompt to influence how the LLM responds to the final user query using the retrieved results. At this step, the model receives:

* The user's original query
* Retrieved document chunks (with metadata)
* The generation system prompt

The model uses these inputs to generate a context-aware response.

### Example

```text
You are a helpful AI assistant specialized in answering questions using retrieved documents.
Your task is to provide accurate, relevant answers based on the matched content provided.
For each query, you will receive:
User's question/query
A set of matched documents, each containing:
  - File name
  - File content


You should:
1. Analyze the relevance of matched documents
2. Synthesize information from multiple sources when applicable
3. Acknowledge if the available documents don't fully answer the query
4. Format the response in a way that maximizes readability, in Markdown format


Answer only with direct reply to the user question, be concise, omit everything which is not directly relevant, focus on answering the question directly and do not redirect the user to read the content.


If the available documents don't contain enough information to fully answer the query, explicitly state this and provide an answer based on what is available.


Important:
- Cite which document(s) you're drawing information from
- Present information in order of relevance
- If documents contradict each other, note this and explain your reasoning for the chosen answer
- Do not repeat the instructions
```

## Query rewriting system prompt

If query rewriting is enabled, you can provide a custom system prompt to control how the model rewrites user queries. In this step, the model receives:

* The query rewrite system prompt
* The original user query

The model outputs a rewritten query optimized for semantic retrieval.

### Example

```text
You are a search query optimizer for vector database searches. Your task is to reformulate user queries into more effective search terms.


Given a user's search query, you must:
1. Identify the core concepts and intent
2. Add relevant synonyms and related terms
3. Remove irrelevant filler words
4. Structure the query to emphasize key terms
5. Include technical or domain-specific terminology if applicable


Provide only the optimized search query without any explanations, greetings, or additional commentary.


Example input: "how to fix a bike tire that's gone flat"
Example output: "bicycle tire repair puncture fix patch inflate maintenance flat tire inner tube replacement"


Constraints:
- Output only the enhanced search terms
- Keep focus on searchable concepts
- Include both specific and general related terms
- Maintain all important meaning from original query
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/#page","headline":"System prompt · Cloudflare AI Search docs","description":"Guide AI Search query rewriting and response generation behavior with custom system prompts.","url":"https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/configuration/","name":"Configuration"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/configuration/retrieval/","name":"Retrieval"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/configuration/retrieval/system-prompt/","name":"System prompt"}}]}
```

---

---
title: How to
description: Step-by-step guides for building search engines, per-tenant search, and other AI Search use cases.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# How to

These guides walk through common AI Search use cases, from no-code setups to full agent applications. Use the difficulty column to find the right place to start.

| Guide                                                                                                                            | Difficulty   | What you build                                                                                           |
| -------------------------------------------------------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------- |
| [Add search to your website](https://developers.cloudflare.com/ai-search/how-to/add-search-to-your-website/)                     | Beginner     | Add a search bar, chat bubble, and search modal to a site with the prebuilt UI snippets.                 |
| [Connect your AI Search to an MCP client](https://developers.cloudflare.com/ai-search/how-to/connect-mcp-client/)                | Beginner     | Expose your indexed content as a search tool for any MCP client or AI assistant, with no code.           |
| [Create a simple search engine](https://developers.cloudflare.com/ai-search/how-to/simple-search-engine/)                        | Beginner     | Query an instance from a Worker using the search binding.                                                |
| [Multitenancy](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/)                                            | Intermediate | Keep each tenant's data separate with a per-tenant instance or a shared instance and metadata filtering. |
| [Search multiple sources at once](https://developers.cloudflare.com/ai-search/how-to/search-multiple-sources/)                   | Intermediate | Search a shared knowledge base and a tenant-specific one together in a single query.                     |
| [Fetch and index single web pages](https://developers.cloudflare.com/ai-search/how-to/fetch-and-index-web-pages/)                | Intermediate | Use Browser Run to fetch a page's rendered HTML, index it, and search it from one Worker.                |
| [Bring your own generation model](https://developers.cloudflare.com/ai-search/how-to/bring-your-own-generation-model/)           | Intermediate | Use AI Search for retrieval and an external model, such as OpenAI, for generation.                       |
| [Show source citations in responses](https://developers.cloudflare.com/ai-search/how-to/chunk-citations/)                        | Intermediate | Return AI-generated answers with citations to the source documents that informed them.                   |
| [Human-in-the-loop knowledge base updates](https://developers.cloudflare.com/ai-search/how-to/human-in-the-loop-knowledge-base/) | Advanced     | Build an agent that proposes new documents and pauses for human approval, with rollback.                 |

```json
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/ai-search/how-to/#page","headline":"How to · Cloudflare AI Search docs","description":"Step-by-step guides for building search engines, per-tenant search, and other AI Search use cases.","url":"https://developers.cloudflare.com/ai-search/how-to/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}}]}
```

---

---
title: Add search to your website
description: Create an AI Search instance that indexes your website, then add a search bar, chat bubble, and search modal to your React site with the UI snippet components.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Add search to your website

This tutorial creates an AI Search instance that indexes your website, then adds a working search bar, chat bubble, and search modal to your site's frontend. It uses the [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/), pre-built web components that connect to your instance's public endpoint, so you add search with only a few lines of frontend code.

**What you will build:** An AI Search instance that indexes your website, and a search bar, chat bubble, and search modal added to your site's frontend that query that content.

![The AI Search modal opened over a site, showing a search input, keyboard navigation hints, and a Powered by Cloudflare AI Search label.](https://developers.cloudflare.com/_astro/ui-snippet-search-modal.nSXbvcsi_1H402.webp) 

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

This tutorial adds search to an existing React app. If you are starting a new project, scaffold one first with the [React framework guide](https://developers.cloudflare.com/workers/framework-guides/web-apps/react/), then follow the next steps. The snippets are framework-agnostic web components, so the same approach works in other frameworks or plain HTML, as shown in [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/).

## 1\. Create an AI Search instance

Create an instance with the [Wrangler CLI](https://developers.cloudflare.com/ai-search/wrangler-commands/). To index a website you own, connect it as a data source so AI Search crawls and indexes it automatically:

 npm  yarn  pnpm 

```
npx wrangler ai-search create my-search --type web-crawler --source <YOUR_DOMAIN>
```

```
yarn wrangler ai-search create my-search --type web-crawler --source <YOUR_DOMAIN>
```

```
pnpm wrangler ai-search create my-search --type web-crawler --source <YOUR_DOMAIN>
```

Replace `<YOUR_DOMAIN>` with a domain [onboarded to your Cloudflare account](https://developers.cloudflare.com/ai-search/configuration/data-source/website/). To create an instance without a data source and upload files yourself instead, run `npx wrangler ai-search create my-search --type builtin` and add content from the [dashboard](https://developers.cloudflare.com/ai-search/get-started/dashboard/#upload-content).

Check indexing progress:

 npm  yarn  pnpm 

```
npx wrangler ai-search stats my-search
```

```
yarn wrangler ai-search stats my-search
```

```
pnpm wrangler ai-search stats my-search
```

Once indexing completes, you can test a query from the command line:

 npm  yarn  pnpm 

```
npx wrangler ai-search search my-search --query 'What is this site about?'
```

```
yarn wrangler ai-search search my-search --query 'What is this site about?'
```

```
pnpm wrangler ai-search search my-search --query 'What is this site about?'
```

## 2\. Enable the public endpoint

The UI snippets connect to your instance through its public endpoint.

1. Go to **AI Search** in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your `my-search` instance.
3. Go to **Settings** \> **Public Endpoint**.
4. Turn on **Enable Public Endpoint**.
5. Copy the public endpoint ID from the URL, `https://<INSTANCE_ID>.search.ai.cloudflare.com/`. You will use it in the next steps.

## 3\. Install the snippet library

In your website's project, install the [AI Search UI snippet library](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/):

 npm  yarn  pnpm  bun 

```
npm i @cloudflare/ai-search-snippet
```

```
yarn add @cloudflare/ai-search-snippet
```

```
pnpm add @cloudflare/ai-search-snippet
```

```
bun add @cloudflare/ai-search-snippet
```

## 4\. Add the search components

Import the snippet library in one of your components and add the tags where you want search to appear. Importing the package once registers the components with the browser. The following example adds a search bar, a floating chat bubble, and a search modal that opens with `Cmd/Ctrl+K` to the app's root component. Replace `<INSTANCE_ID>` with your public endpoint ID from step two.

**src/App.tsx**

```tsx
import "@cloudflare/ai-search-snippet";


export default function App() {
  return (
    <div>
      <search-bar-snippet
        api-url="https://<INSTANCE_ID>.search.ai.cloudflare.com/"
        placeholder="Search..."
        max-results={50}
        max-render-results={10}
        show-url="true"
        show-date="true"
      />
      <chat-bubble-snippet
        api-url="https://<INSTANCE_ID>.search.ai.cloudflare.com/"
        style={
          {
            "--search-snippet-primary-color": "#F6821F",
          } as React.CSSProperties
        }
      />
      <search-modal-snippet
        api-url="https://<INSTANCE_ID>.search.ai.cloudflare.com/"
        placeholder="Search documentation..."
        shortcut="k"
        show-url="true"
        show-date="true"
      />
    </div>
  );
}
```

### Add TypeScript declarations for the custom elements

The snippet package ships type definitions for its classes, but it does not tell TypeScript that `<search-bar-snippet>` and the other tags are valid JSX elements. The Vite development server does not type-check, so the app runs without this step, but adding a declaration file keeps `.tsx` type checks and editors from reporting errors on the custom tags.

Create a declaration file such as `src/ai-search-snippet.d.ts`:

**src/ai-search-snippet.d.ts**

```ts
import type { HTMLAttributes } from "react";


// Register the snippet web components as valid JSX elements. The index
// signature allows their custom attributes (such as api-url and placeholder).
type SnippetElement = HTMLAttributes<HTMLElement> & {
  [attribute: string]: unknown;
};


declare module "react" {
  namespace JSX {
    interface IntrinsicElements {
      "search-bar-snippet": SnippetElement;
      "chat-bubble-snippet": SnippetElement;
      "search-modal-snippet": SnippetElement;
    }
  }
}
```

This types the tags loosely so any attribute is allowed. For stricter, per-component types, refer to the [React demo declarations ↗](https://github.com/cloudflare/ai-search-snippet/blob/main/apps/demo-react/index.d.ts) in the snippet repository.

## 5\. Allow your local origin

The public endpoint uses CORS to control which sites can call it. Add the origin your site runs on during local development so the browser can reach the endpoint. A Vite app runs on `http://localhost:5173`.

1. In your AI Search instance, go to **Settings** \> **Public Endpoint**.
2. Under **Authorized hosts**, add your local origin, for example `http://localhost:5173`.
3. Select **Save**.

## 6\. Test it

Start your development server:

 npm  yarn  pnpm 

```
npm run dev
```

```
yarn run dev
```

```
pnpm run dev
```

Open your site in the browser (a Vite app runs on `http://localhost:5173`). Type in the search bar to see results in a dropdown, select the chat bubble in the corner to ask a question, or press `Cmd/Ctrl+K` to open the search modal. For the full set of components, attributes, and theming options, refer to [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/).

## 7\. Go to production

The snippets work anywhere your site is served. When you deploy your site to its production domain, return to **Settings** \> **Public Endpoint** and add that origin to **Authorized hosts** (as in step five), so the browser can reach the endpoint in production.

## Next steps

[ UI snippets ](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/) All snippet components, attributes, and CSS theming options. 

[ Wrangler commands ](https://developers.cloudflare.com/ai-search/wrangler-commands/) Manage AI Search instances from the command line. 

[ Public endpoint settings ](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/) Rate limiting, CORS, and tool description for the public endpoint.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/add-search-to-your-website/#page","headline":"Add search to your website · Cloudflare AI Search docs","description":"Create an AI Search instance that indexes your website, then add a search bar, chat bubble, and search modal to your React site with the UI snippet components.","url":"https://developers.cloudflare.com/ai-search/how-to/add-search-to-your-website/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/add-search-to-your-website/","name":"Add search to your website"}}]}
```

---

---
title: Bring your own generation model
description: Use AI Search for retrieval while generating responses with an external model like OpenAI.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Bring your own generation model

By default, AI Search uses a Workers AI model to generate responses. To use a model outside of Workers AI, use AI Search for `search` and pass the retrieved content to a different model for generation. This guide uses an OpenAI model.

Note

AI Search supports [bringing your own models natively](https://developers.cloudflare.com/ai-search/configuration/models/). You can attach provider keys through AI Gateway and select third-party models directly in your AI Search settings. The example below still works, but the recommended approach is to configure your external model through AI Gateway.

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

You also need:

* An AI Search instance that already contains indexed content. To create one and add content, refer to [Get started](https://developers.cloudflare.com/ai-search/get-started/).
* An [OpenAI API key ↗](https://platform.openai.com/api-keys).

## 1\. Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `byo-model` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- byo-model
```

```
yarn create cloudflare byo-model
```

```
pnpm create cloudflare@latest byo-model
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd byo-model
```

## 2\. Install the AI SDK and OpenAI provider

Install the [AI SDK ↗](https://sdk.vercel.ai/) and its OpenAI provider:

 npm  yarn  pnpm  bun 

```
npm i ai @ai-sdk/openai
```

```
yarn add ai @ai-sdk/openai
```

```
pnpm add ai @ai-sdk/openai
```

```
bun add ai @ai-sdk/openai
```

## 3\. Bind your Worker and set your API key

Add the AI Search binding to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/):

* [  wrangler.jsonc ](#tab-panel-7207)
* [  wrangler.toml ](#tab-panel-7208)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true
```

Store your OpenAI API key as a [secret](https://developers.cloudflare.com/workers/configuration/secrets/):

```sh
npx wrangler secret put OPENAI_API_KEY
```

For local development, add the key to a `.dev.vars` file in your project root instead:

**.dev.vars**

```txt
OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"
```

## 4\. Add the code

Update `src/index.ts`. This Worker searches your instance, formats the retrieved chunks, and passes them to OpenAI to generate an answer. Replace `my-instance` with the name of your instance.

* [  JavaScript ](#tab-panel-7209)
* [  TypeScript ](#tab-panel-7210)

**src/index.js**

```js
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";


export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const userQuery = url.searchParams.get("query") ?? "What is Cloudflare?";


    // Search for documents in AI Search.
    const searchResult = await env.AI_SEARCH.get("my-instance").search({
      messages: [{ role: "user", content: userQuery }],
    });


    if (searchResult.chunks.length === 0) {
      return Response.json({ text: `No data found for query "${userQuery}"` });
    }


    // Join the retrieved chunks into a single string.
    const chunks = searchResult.chunks
      .map((chunk) => `<file name="${chunk.item.key}">${chunk.text}</file>`)
      .join("\n\n");


    // Send the query and retrieved content to OpenAI for the answer.
    const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY });
    const generateResult = await generateText({
      model: openai("gpt-4o-mini"),
      messages: [
        {
          role: "system",
          content:
            "You are a helpful assistant. Answer the user question using the provided files.",
        },
        { role: "user", content: chunks },
        { role: "user", content: userQuery },
      ],
    });


    return Response.json({ text: generateResult.text });
  },
};
```

**src/index.ts**

```ts
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";


export interface Env {
  AI_SEARCH: AiSearchNamespace;
  OPENAI_API_KEY: string;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    const userQuery = url.searchParams.get("query") ?? "What is Cloudflare?";


    // Search for documents in AI Search.
    const searchResult = await env.AI_SEARCH.get("my-instance").search({
      messages: [{ role: "user", content: userQuery }],
    });


    if (searchResult.chunks.length === 0) {
      return Response.json({ text: `No data found for query "${userQuery}"` });
    }


    // Join the retrieved chunks into a single string.
    const chunks = searchResult.chunks
      .map((chunk) => `<file name="${chunk.item.key}">${chunk.text}</file>`)
      .join("\n\n");


    // Send the query and retrieved content to OpenAI for the answer.
    const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY });
    const generateResult = await generateText({
      model: openai("gpt-4o-mini"),
      messages: [
        {
          role: "system",
          content:
            "You are a helpful assistant. Answer the user question using the provided files.",
        },
        { role: "user", content: chunks },
        { role: "user", content: userQuery },
      ],
    });


    return Response.json({ text: generateResult.text });
  },
} satisfies ExportedHandler<Env>;
```

## 5\. Run and deploy

Start a local development server, then query it at `/?query=your+search+terms`:

```sh
npx wrangler dev
```

Log in with your Cloudflare account, then deploy your Worker to make it accessible on the Internet:

```sh
npx wrangler login
npx wrangler deploy
```

## Next steps

[ Models ](https://developers.cloudflare.com/ai-search/configuration/models/) Use third-party models natively through AI Gateway. 

[ Search Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Full reference for searching and chatting from a Worker.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/bring-your-own-generation-model/#page","headline":"Bring your own generation model · Cloudflare AI Search docs","description":"Use AI Search for retrieval while generating responses with an external model like OpenAI.","url":"https://developers.cloudflare.com/ai-search/how-to/bring-your-own-generation-model/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"},"keywords":["AI"]}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/bring-your-own-generation-model/","name":"Bring your own generation model"}}]}
```

---

---
title: Show source citations in responses
description: Display source citations alongside AI-generated answers.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Show source citations in responses

[AI Search](https://developers.cloudflare.com/ai-search/) returns the source chunks it uses to generate an answer. Use those chunks to show citations, references, or source links in your application.

This guide shows how to build a [Cloudflare Worker](https://developers.cloudflare.com/workers/) that returns an AI-generated answer with the documents that informed it. Use this pattern when you want users to verify answers, inspect source material, or debug retrieval quality.

## What you will build

You will create a Worker endpoint that:

* Sends a user question to `chatCompletions()`
* Returns the generated answer with source identifiers, snippets, metadata, and relevance scores
* Groups repeated chunks into one citation per source document
* Handles citations for standard and streaming responses

## How citations work

AI Search retrieves source chunks before it generates an answer:

1. Finds matching chunks from your indexed documents.
2. Sends those chunks to the model as context.
3. Returns the answer and chunks in the response.

Each returned chunk contains an `item` object with `key` (filename or URL), `timestamp`, and any custom `metadata` you attached during indexing. For citations, `item.key` is usually the most useful field because it identifies the source document.

The `score` field indicates how relevant the chunk was to the query. The `chunks` array is also available in the `search()` response, and the same approach applies.

## 1\. Create a Worker

Create a Worker project for the citation examples:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- ai-search-citations
```

```
yarn create cloudflare ai-search-citations
```

```
pnpm create cloudflare@latest ai-search-citations
```

When prompted, choose **Hello World example**, **Worker only**, and **TypeScript**.

Move into the project directory:

```sh
cd ai-search-citations
```

## 2\. Configure the binding

Add an AI Search namespace binding to your Wrangler configuration:

* [  wrangler.jsonc ](#tab-panel-7211)
* [  wrangler.toml ](#tab-panel-7212)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "ai-search-citations",
  "main": "src/index.ts",
  // Set this to today's date
  "compatibility_date": "2026-07-14",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default"
    }
  ]
}
```

**TOML**

```toml
name = "ai-search-citations"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-07-14"


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
```

This binding lets your Worker access AI Search instances in the `default` namespace. The examples use an instance named `my-instance`.

If you do not have an instance yet, create one and add content before you run the Worker. To create an instance with Wrangler, refer to [Wrangler commands](https://developers.cloudflare.com/ai-search/get-started/wrangler/).

## 3\. Display citations from chat completions

Start with the simplest citation pattern: return the generated answer and a list of source documents in the same JSON response.

Replace the contents of `src/index.ts` with the following Worker code:

* [  JavaScript ](#tab-panel-7213)
* [  TypeScript ](#tab-panel-7214)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // AI Search returns an answer and the source chunks used as context.
    const response = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
    });


    // Show this model response to the user.
    const answer = response.choices[0]?.message?.content ?? "";


    // Convert source chunks into citations your UI can display.
    const citations = response.chunks.map((chunk, index) => ({
      index: index + 1,
      source: chunk.item.key,
      score: chunk.score,
      snippet: chunk.text.slice(0, 200),
      metadata: chunk.item.metadata,
    }));


    return Response.json({ answer, citations });
  },
};
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // AI Search returns an answer and the source chunks used as context.
    const response = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
    });


    // Show this model response to the user.
    const answer = response.choices[0]?.message?.content ?? "";


    // Convert source chunks into citations your UI can display.
    const citations = response.chunks.map((chunk, index) => ({
      index: index + 1,
      source: chunk.item.key,
      score: chunk.score,
      snippet: chunk.text.slice(0, 200),
      metadata: chunk.item.metadata,
    }));


    return Response.json({ answer, citations });
  },
} satisfies ExportedHandler<Env>;
```

The response looks like:

```json
{
  "answer": "Cloudflare is a global network that provides security, performance, and reliability services...",
  "citations": [
    {
      "index": 1,
      "source": "docs/what-is-cloudflare.md",
      "score": 0.92,
      "snippet": "Cloudflare is one of the world's largest networks. Today, businesses, non-profits, bloggers...",
      "metadata": {
        "folder": "docs"
      }
    },
    {
      "index": 2,
      "source": "blog/intro-to-cloudflare.md",
      "score": 0.85,
      "snippet": "Cloudflare provides a broad range of services to businesses of all sizes...",
      "metadata": {
        "folder": "blog"
      }
    }
  ]
}
```

## 4\. Deduplicate citations by source

Multiple chunks can come from the same document. Group them by `item.key` to show one citation per source document.

To show one citation per source, update `src/index.ts` to group chunks by source document:

* [  JavaScript ](#tab-panel-7217)
* [  TypeScript ](#tab-panel-7218)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // AI Search returns an answer and the source chunks used as context.
    const response = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
    });


    // Show this model response to the user.
    const answer = response.choices[0]?.message?.content ?? "";


    // Group chunks by source document so each source appears once.
    const sourceMap = new Map();


    for (const chunk of response.chunks) {
      // item.key is the source file path or URL.
      const key = chunk.item.key;
      const existing = sourceMap.get(key);


      if (existing) {
        // Keep the highest relevance score for each source.
        existing.score = Math.max(existing.score, chunk.score);
        existing.snippets.push(chunk.text.slice(0, 200));
      } else {
        sourceMap.set(key, {
          score: chunk.score,
          snippets: [chunk.text.slice(0, 200)],
          metadata: chunk.item.metadata,
        });
      }
    }


    const citations = [...sourceMap.entries()].map(
      ([source, { score, snippets, metadata }], i) => ({
        index: i + 1,
        source,
        score,
        snippets,
        metadata,
      }),
    );


    return Response.json({ answer, citations });
  },
};
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // AI Search returns an answer and the source chunks used as context.
    const response = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
    });


    // Show this model response to the user.
    const answer = response.choices[0]?.message?.content ?? "";


    // Group chunks by source document so each source appears once.
    const sourceMap = new Map<
      string,
      { score: number; snippets: string[]; metadata?: Record<string, unknown> }
    >();


    for (const chunk of response.chunks) {
      // item.key is the source file path or URL.
      const key = chunk.item.key;
      const existing = sourceMap.get(key);


      if (existing) {
        // Keep the highest relevance score for each source.
        existing.score = Math.max(existing.score, chunk.score);
        existing.snippets.push(chunk.text.slice(0, 200));
      } else {
        sourceMap.set(key, {
          score: chunk.score,
          snippets: [chunk.text.slice(0, 200)],
          metadata: chunk.item.metadata,
        });
      }
    }


    const citations = [...sourceMap.entries()].map(
      ([source, { score, snippets, metadata }], i) => ({
        index: i + 1,
        source,
        score,
        snippets,
        metadata,
      }),
    );


    return Response.json({ answer, citations });
  },
} satisfies ExportedHandler<Env>;
```

## 5\. Parse citations from a streaming response

When using `stream: true`, the chunks are sent as a separate Server-Sent Events (SSE) event named `chunks` before the streamed answer begins. Parse this event to show citations before the full answer finishes streaming.

To show citations before the full answer finishes streaming, update `src/index.ts` to transform the stream:

* [  JavaScript ](#tab-panel-7219)
* [  TypeScript ](#tab-panel-7220)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // Stream answer tokens, but extract source chunks first.
    const stream = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
      stream: true,
    });


    // Transform the stream: extract the chunks event and forward the rest
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();
    const encoder = new TextEncoder();
    const decoder = new TextDecoder();
    const reader = stream.getReader();


    // Track the current SSE event type to identify source chunks.
    let currentEvent = "";


    const pump = async () => {
      try {
        let buffer = "";


        while (true) {
          const { done, value } = await reader.read();
          if (done) break;


          buffer += decoder.decode(value, { stream: true });
          const lines = buffer.split("\n");
          buffer = lines.pop() ?? "";


          for (const line of lines) {
            // The chunks event arrives before the streamed answer.
            if (line.startsWith("event: ")) {
              currentEvent = line.slice(7).trim();
              continue;
            }


            // Transform the chunks data line into a citations event for your UI.
            if (currentEvent === "chunks" && line.startsWith("data: ")) {
              const chunks = JSON.parse(line.slice(6));
              const citations = chunks.map((chunk) => ({
                source: chunk.item.key,
                score: chunk.score,
              }));
              await writer.write(
                encoder.encode(
                  `event: citations\ndata: ${JSON.stringify(citations)}\n\n`,
                ),
              );
              currentEvent = "";
              continue;
            }


            // Forward answer tokens and other SSE data unchanged.
            currentEvent = "";
            await writer.write(encoder.encode(line + "\n"));
          }
        }
      } finally {
        reader.releaseLock();
        await writer.close();
      }
    };


    pump().catch(() => writer.close());


    return new Response(readable, {
      headers: {
        "content-type": "text/event-stream",
        "cache-control": "no-cache",
      },
    });
  },
};
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // Stream answer tokens, but extract source chunks first.
    const stream = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
      stream: true,
    });


    // Transform the stream: extract the chunks event and forward the rest
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();
    const encoder = new TextEncoder();
    const decoder = new TextDecoder();
    const reader = stream.getReader();


    // Track the current SSE event type to identify source chunks.
    let currentEvent = "";


    const pump = async () => {
      try {
        let buffer = "";


        while (true) {
          const { done, value } = await reader.read();
          if (done) break;


          buffer += decoder.decode(value, { stream: true });
          const lines = buffer.split("\n");
          buffer = lines.pop() ?? "";


          for (const line of lines) {
            // The chunks event arrives before the streamed answer.
            if (line.startsWith("event: ")) {
              currentEvent = line.slice(7).trim();
              continue;
            }


            // Transform the chunks data line into a citations event for your UI.
            if (currentEvent === "chunks" && line.startsWith("data: ")) {
              const chunks = JSON.parse(line.slice(6));
              const citations = chunks.map(
                (chunk: { item: { key: string }; score: number }) => ({
                  source: chunk.item.key,
                  score: chunk.score,
                }),
              );
              await writer.write(
                encoder.encode(
                  `event: citations\ndata: ${JSON.stringify(citations)}\n\n`,
                ),
              );
              currentEvent = "";
              continue;
            }


            // Forward answer tokens and other SSE data unchanged.
            currentEvent = "";
            await writer.write(encoder.encode(line + "\n"));
          }
        }
      } finally {
        reader.releaseLock();
        await writer.close();
      }
    };


    pump().catch(() => writer.close());


    return new Response(readable, {
      headers: {
        "content-type": "text/event-stream",
        "cache-control": "no-cache",
      },
    });
  },
} satisfies ExportedHandler<Env>;
```

## 6\. Use scoring details to rank citations

Each chunk includes a `scoring_details` object with a breakdown of how it was scored. Use these details to filter out low-quality citations or display confidence indicators.

To filter citations by relevance, update `src/index.ts` to use score fields:

* [  JavaScript ](#tab-panel-7215)
* [  TypeScript ](#tab-panel-7216)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // AI Search returns scoring details with each source chunk.
    const response = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
    });


    // Show this model response to the user.
    const answer = response.choices[0]?.message?.content ?? "";


    const citations = response.chunks
      // Filter out lower-scoring chunks for stronger citations.
      .filter((chunk) => chunk.score > 0.5)
      // Expose scoring details if your UI shows confidence indicators.
      .map((chunk, index) => ({
        index: index + 1,
        source: chunk.item.key,
        score: chunk.score,
        vectorScore: chunk.scoring_details?.vector_score,
        keywordScore: chunk.scoring_details?.keyword_score,
        rerankingScore: chunk.scoring_details?.reranking_score,
        confidence: chunk.score > 0.8 ? "high" : "medium",
        snippet: chunk.text.slice(0, 200),
      }));


    return Response.json({ answer, citations });
  },
};
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    const query = url.searchParams.get("query") ?? "What is Cloudflare?";


    // AI Search returns scoring details with each source chunk.
    const response = await env.AI_SEARCH.get("my-instance").chatCompletions({
      messages: [{ role: "user", content: query }],
    });


    // Show this model response to the user.
    const answer = response.choices[0]?.message?.content ?? "";


    const citations = response.chunks
      // Filter out lower-scoring chunks for stronger citations.
      .filter((chunk) => chunk.score > 0.5)
      // Expose scoring details if your UI shows confidence indicators.
      .map((chunk, index) => ({
        index: index + 1,
        source: chunk.item.key,
        score: chunk.score,
        vectorScore: chunk.scoring_details?.vector_score,
        keywordScore: chunk.scoring_details?.keyword_score,
        rerankingScore: chunk.scoring_details?.reranking_score,
        confidence: chunk.score > 0.8 ? "high" : "medium",
        snippet: chunk.text.slice(0, 200),
      }));


    return Response.json({ answer, citations });
  },
} satisfies ExportedHandler<Env>;
```

## Use citation fields

Each chunk in the `chunks` array can include the following fields:

| Field                             | Type   | Description                                                               |
| --------------------------------- | ------ | ------------------------------------------------------------------------- |
| id                                | string | Unique identifier for the chunk.                                          |
| type                              | string | Content type, typically text.                                             |
| score                             | number | Overall relevance score between 0 and 1.                                  |
| text                              | string | The text content of the chunk.                                            |
| item.key                          | string | The file path or URL of the source document.                              |
| item.timestamp                    | number | Unix timestamp of when the item was last indexed.                         |
| item.metadata                     | object | Custom metadata associated with the source item.                          |
| scoring\_details.vector\_score    | number | Semantic similarity score (0 to 1).                                       |
| scoring\_details.keyword\_score   | number | BM25 keyword match score. Present when using hybrid or keyword retrieval. |
| scoring\_details.keyword\_rank    | number | Keyword rank position.                                                    |
| scoring\_details.vector\_rank     | number | Vector rank position.                                                     |
| scoring\_details.reranking\_score | number | Reranking score (0 to 1). Present when reranking is enabled.              |
| scoring\_details.fusion\_method   | string | Fusion method used (rrf or max). Present when using hybrid retrieval.     |

For multi-instance searches, each chunk also includes an `instance_id` field identifying which instance it came from. To search or chat across multiple instances, refer to [namespace methods](https://developers.cloudflare.com/ai-search/api/search/workers-binding/#namespace-methods).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/chunk-citations/#page","headline":"Show source citations in responses · Cloudflare AI Search docs","description":"Display source citations alongside AI-generated answers.","url":"https://developers.cloudflare.com/ai-search/how-to/chunk-citations/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/chunk-citations/","name":"Show source citations in responses"}}]}
```

---

---
title: Connect your AI Search to an MCP client
description: Expose your indexed content as a search tool for any MCP client or agent using the built-in MCP endpoint.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Connect your AI Search to an MCP client

Every AI Search instance can expose a built-in [Model Context Protocol (MCP) ↗](https://modelcontextprotocol.io/) endpoint. The endpoint provides a `search` tool over your indexed content, so any MCP client or agent, such as an AI assistant or IDE, can search your knowledge base without any code.

This guide creates an AI Search instance that indexes a documentation site, then exposes it as a search tool that any MCP client can call.

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

To index a website, you also need a domain [onboarded to your Cloudflare account](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/). Otherwise, you can upload your own files to [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/).

## 1\. Create an AI Search instance

If you already have an instance with indexed content, skip to [step 2](#2-enable-the-mcp-endpoint).

Create an instance with the [Wrangler CLI](https://developers.cloudflare.com/ai-search/wrangler-commands/). This example indexes a documentation site, using the Cloudflare Developer Docs at `developers.cloudflare.com`, so an assistant can answer questions from it. Connect the site as a [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) so AI Search crawls and indexes it automatically:

```sh
npx wrangler ai-search create docs-search --type web-crawler --source developers.cloudflare.com
```

Replace `developers.cloudflare.com` with a domain you have [onboarded to your Cloudflare account](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/), since you can only crawl sites you own. To index content without crawling a site, run `npx wrangler ai-search create docs-search --type builtin` and upload files to [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) instead.

Check indexing progress:

```sh
npx wrangler ai-search stats docs-search
```

Once indexing completes, your instance has content to expose over MCP.

## 2\. Enable the MCP endpoint

Your instance's public endpoint serves the MCP endpoint.

1. Go to **AI Search** in the Cloudflare dashboard.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your `docs-search` instance.
3. Go to **Settings** \> **Public Endpoint**.
4. Turn on **Enable Public Endpoint**, then turn on the **MCP** endpoint.
5. Copy the endpoint host. Your MCP URL is that host followed by `/mcp`:  
```txt  
https://<INSTANCE_ID>.search.ai.cloudflare.com/mcp  
```

## 3\. Describe your search tool

An MCP client reads a tool's description to decide when to call it. Under **Settings** \> **Public Endpoint**, set the **Tool Description** to explain what your content covers and the questions it answers. For example:

```txt
Search the Cloudflare Developer Documentation for product concepts,
configuration, and API references. Use this when users ask how to build
or configure Cloudflare products.
```

A specific description helps agents call your search tool at the right time. Refer to [Public endpoint settings](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/) for the full configuration.

## 4\. Connect your MCP client

Add the MCP URL to your client as a remote MCP server. Many clients use an `mcpServers` configuration like the following:

```json
{
  "mcpServers": {
    "ai-search": {
      "url": "https://<INSTANCE_ID>.search.ai.cloudflare.com/mcp"
    }
  }
}
```

The exact configuration depends on your client. Some clients require a transport field on the server entry, such as `"type": "http"` for a remote HTTP server, so refer to your MCP client's documentation for how to add a remote server. Once connected, the client can call the `search` tool to retrieve relevant content from your instance.

To test the endpoint directly or build your own client, refer to [MCP](https://developers.cloudflare.com/ai-search/api/search/mcp/) for the request format.

## Security considerations

The public endpoint does not require authentication, so anyone with the URL can query your indexed content. To control access:

* Enable rate limiting under **Settings** \> **Public Endpoint**.
* Restrict allowed origins with CORS rules.
* Only index content that is safe to expose publicly.

Refer to [Public endpoint settings](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/) for details.

## Next steps

[ MCP endpoint reference ](https://developers.cloudflare.com/ai-search/api/search/mcp/) The MCP endpoint tools and request format. 

[ Public endpoint settings ](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/) Rate limiting, CORS, and tool description. 

[ AI Search as an agent tool ](https://developers.cloudflare.com/agents/tools/ai-search/) Query AI Search in code from a Cloudflare Agent.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/connect-mcp-client/#page","headline":"Connect your AI Search to an MCP client · Cloudflare AI Search docs","description":"Expose your indexed content as a search tool for any MCP client or agent using the built-in MCP endpoint.","url":"https://developers.cloudflare.com/ai-search/how-to/connect-mcp-client/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/connect-mcp-client/","name":"Connect your AI Search to an MCP client"}}]}
```

---

---
title: Fetch and index single web pages
description: Use the Browser Run /content endpoint to fetch a single web page's rendered HTML, then upload it to an AI Search instance's built-in storage so AI Search indexes it for search.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Fetch and index single web pages

This guide builds a Worker that fetches a single web page's rendered HTML with the [Browser Run](https://developers.cloudflare.com/browser-run/) [/content endpoint](https://developers.cloudflare.com/browser-run/quick-actions/content-endpoint/) and uploads it to an [AI Search](https://developers.cloudflare.com/ai-search/) instance's [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) using the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/). AI Search then indexes the page so it is searchable, the same as any other uploaded document. The Worker also exposes a `/search` endpoint that queries the indexed pages, so one service both indexes and searches.

## When to use this pattern

Use this pattern to index one page, or a small hand-picked set of pages, on demand. To crawl and continuously index an entire site, use the AI Search [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) instead.

Both Browser Run and the AI Search instance are reached through bindings, so a single Worker can fetch a page and index it without a public endpoint in between.

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

You also need an AI Search instance to upload to. To create one, refer to [Get started](https://developers.cloudflare.com/ai-search/get-started/). This guide uploads to the instance's built-in storage, so the instance does not need an external data source.

## 1\. Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `fetch-and-index` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- fetch-and-index
```

```
yarn create cloudflare fetch-and-index
```

```
pnpm create cloudflare@latest fetch-and-index
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd fetch-and-index
```

## 2\. Configure Wrangler

Add both bindings to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/): a [browser binding](https://developers.cloudflare.com/browser-run/reference/wrangler/#bindings) for Browser Run and an [AI Search namespace binding](https://developers.cloudflare.com/ai-search/api/items/workers-binding/) for uploads. The `/content` endpoint runs through the browser binding, so you do not need to install Puppeteer or any other package.

* [  wrangler.jsonc ](#tab-panel-7221)
* [  wrangler.toml ](#tab-panel-7222)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "fetch-and-index",
  "main": "src/index.ts",
  // Set this to today's date
  "compatibility_date": "2026-07-14",
  "browser": {
    "binding": "BROWSER",
    "remote": true
  },
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
name = "fetch-and-index"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-07-14"


[browser]
binding = "BROWSER"
remote = true


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true
```

The browser binding's `quickAction` method requires a compatibility date of `2026-03-24` or later, and is not supported in local development without remote mode. Setting `remote = true` on the browser binding enables remote mode for `wrangler dev`. The `remote` option on the AI Search binding proxies uploads to your deployed instance, since AI Search does not run locally.

## 3\. Add the Worker code

Update `src/index.ts`. This Worker has two routes: a request with a `?url=` parameter fetches that page's rendered HTML and indexes it, and a request to `/search?q=` queries the indexed content. Replace `my-instance` with the name of your instance.

* [  JavaScript ](#tab-panel-7223)
* [  TypeScript ](#tab-panel-7224)

**src/index.js**

```js
// The instance that indexes the fetched page.
const INSTANCE_ID = "my-instance";


// Build a stable item key that ends in .html, so AI Search converts the HTML
// to Markdown before indexing it.
function itemKey(pageUrl) {
  const slug = `${pageUrl.hostname}${pageUrl.pathname}`
    .replace(/[^a-zA-Z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
  return `${slug || "index"}.html`;
}


export default {
  async fetch(request, env) {
    const url = new URL(request.url);


    // Search route: query the indexed content and return the matching chunks.
    if (url.pathname === "/search") {
      const query = url.searchParams.get("q");
      if (!query) {
        return new Response("Add a ?q= query parameter", { status: 400 });
      }
      const results = await env.AI_SEARCH.get(INSTANCE_ID).search({ query });
      return Response.json({
        query: results.search_query,
        results: results.chunks.map((chunk) => ({
          key: chunk.item.key,
          score: chunk.score,
          text: chunk.text,
        })),
      });
    }


    // Index route: fetch a URL's rendered HTML and index it.
    const target = url.searchParams.get("url");
    if (!target) {
      return new Response(
        "Add a ?url= parameter to index a page, or use /search?q= to search",
        { status: 400 },
      );
    }


    const pageUrl = new URL(target);


    // Fetch the fully rendered HTML with the Browser Run /content endpoint.
    // networkidle2 waits until the page has no more than two network
    // connections for at least 500 ms, giving client-side JavaScript time
    // to render the content.
    const response = await env.BROWSER.quickAction("content", {
      url: pageUrl.toString(),
      gotoOptions: {
        waitUntil: "networkidle2",
        timeout: 30000,
      },
    });


    if (!response.ok) {
      const detail = (await response.text()).slice(0, 500);
      return new Response(
        `Browser Run failed with ${response.status}: ${detail}`,
        { status: 502 },
      );
    }


    // The /content endpoint returns a JSON envelope with the rendered HTML
    // in the result field.
    const data = await response.json();


    if (!data.success || typeof data.result !== "string") {
      return new Response("Browser Run returned an unsuccessful response", {
        status: 502,
      });
    }


    const html = data.result;


    // Upload the rendered HTML to built-in storage. uploadAndPoll waits until
    // the page is indexed and searchable.
    const item = await env.AI_SEARCH.get(INSTANCE_ID).items.uploadAndPoll(
      itemKey(pageUrl),
      html,
      { timeoutMs: 60_000 },
    );


    return Response.json({ key: item.key, status: item.status });
  },
};
```

**src/index.ts**

```ts
export interface Env {
  BROWSER: BrowserRun;
  AI_SEARCH: AiSearchNamespace;
}


// The instance that indexes the fetched page.
const INSTANCE_ID = "my-instance";


// Build a stable item key that ends in .html, so AI Search converts the HTML
// to Markdown before indexing it.
function itemKey(pageUrl: URL): string {
  const slug = `${pageUrl.hostname}${pageUrl.pathname}`
    .replace(/[^a-zA-Z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
  return `${slug || "index"}.html`;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);


    // Search route: query the indexed content and return the matching chunks.
    if (url.pathname === "/search") {
      const query = url.searchParams.get("q");
      if (!query) {
        return new Response("Add a ?q= query parameter", { status: 400 });
      }
      const results = await env.AI_SEARCH.get(INSTANCE_ID).search({ query });
      return Response.json({
        query: results.search_query,
        results: results.chunks.map((chunk) => ({
          key: chunk.item.key,
          score: chunk.score,
          text: chunk.text,
        })),
      });
    }


    // Index route: fetch a URL's rendered HTML and index it.
    const target = url.searchParams.get("url");
    if (!target) {
      return new Response(
        "Add a ?url= parameter to index a page, or use /search?q= to search",
        { status: 400 },
      );
    }


    const pageUrl = new URL(target);


    // Fetch the fully rendered HTML with the Browser Run /content endpoint.
    // networkidle2 waits until the page has no more than two network
    // connections for at least 500 ms, giving client-side JavaScript time
    // to render the content.
    const response = await env.BROWSER.quickAction("content", {
      url: pageUrl.toString(),
      gotoOptions: {
        waitUntil: "networkidle2",
        timeout: 30000,
      },
    });


    if (!response.ok) {
      const detail = (await response.text()).slice(0, 500);
      return new Response(
        `Browser Run failed with ${response.status}: ${detail}`,
        { status: 502 },
      );
    }


    // The /content endpoint returns a JSON envelope with the rendered HTML
    // in the result field.
    const data = (await response.json()) as {
      success: boolean;
      result?: string;
    };


    if (!data.success || typeof data.result !== "string") {
      return new Response("Browser Run returned an unsuccessful response", {
        status: 502,
      });
    }


    const html = data.result;


    // Upload the rendered HTML to built-in storage. uploadAndPoll waits until
    // the page is indexed and searchable.
    const item = await env.AI_SEARCH.get(INSTANCE_ID).items.uploadAndPoll(
      itemKey(pageUrl),
      html,
      { timeoutMs: 60_000 },
    );


    return Response.json({ key: item.key, status: item.status });
  },
} satisfies ExportedHandler<Env>;
```

The `.html` item key tells AI Search to run the content through [Markdown conversion](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/), which strips boilerplate such as the header and footer before indexing.

Warning

Fetch only URLs you trust. A Worker that fetches arbitrary user-supplied URLs can become an open proxy. Consider restricting the accepted hostnames to an allowlist. Uploaded content is also limited to 4 MB per item.

## 4\. Attach metadata for filtering

This step is optional. Because this Worker controls the upload, you can enrich each page with structured [metadata](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/), such as its title and section, and then [filter searches](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) by those fields. This is something the built-in crawler cannot do on its own.

First, define the custom metadata fields on your instance. If you are creating the instance now, pass them to `create`:

```sh
npx wrangler ai-search create my-instance --type builtin --custom-metadata title:text --custom-metadata section:text
```

To add fields to an existing instance, use the dashboard under **Settings**, or the [update()](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/#update) binding method. An instance supports up to five custom fields, and each field can be a `text`, `number`, `boolean`, or `datetime` type. Changing the schema re-indexes existing documents.

Next, use the Browser Run [/json endpoint](https://developers.cloudflare.com/browser-run/quick-actions/json-endpoint/) to extract those fields from the same page. It runs through the same browser binding and returns structured JSON that matches a schema you provide. In the `fetch` handler from step 3, after you have the rendered `html` and before the upload, add:

**TypeScript**

```ts
// Extract structured metadata from the page with the /json endpoint.
// response_format constrains the model to the fields you defined above.
// Treat extraction as best-effort: if it fails, index the page without metadata.
const metadata: Record<string, string> = {};
try {
  const jsonResponse = await env.BROWSER.quickAction("json", {
    url: pageUrl.toString(),
    prompt: "Extract the page title and its top-level section.",
    response_format: {
      type: "json_schema",
      json_schema: {
        type: "object",
        properties: {
          title: { type: "string" },
          section: { type: "string" },
        },
        required: ["title"],
      },
    },
  });


  const extracted = (await jsonResponse.json()) as {
    result?: Record<string, unknown>;
  };


  // Metadata values must be strings, so coerce each value and drop empty ones.
  for (const [key, value] of Object.entries(extracted.result ?? {})) {
    if (value) metadata[key] = String(value);
  }
} catch {
  // Ignore extraction errors and index the page without metadata.
}
```

Then pass `metadata` in the upload options:

**TypeScript**

```ts
const item = await env.AI_SEARCH.get(INSTANCE_ID).items.uploadAndPoll(
  itemKey(pageUrl),
  html,
  { timeoutMs: 60_000, metadata },
);
```

Once indexed, you can restrict queries to pages in a given section, for example. Refer to [Filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) for the query syntax.

## 5\. Run and deploy

Start a local development server. Because `remote = true` is set on the browser binding, `wrangler dev` runs the `/content` endpoint in remote mode:

```sh
npx wrangler dev
```

Index a page by passing its URL:

```sh
curl "http://localhost:8787/?url=https://example.com/"
```

The response contains the item key and its status (`completed` once indexed):

```json
{ "key": "example-com.html", "status": "completed" }
```

Then query the indexed content through the same Worker's `/search` endpoint:

```sh
curl "http://localhost:8787/search?q=what+is+this+domain+for"
```

```json
{
  "query": "what is this domain for",
  "results": [
    {
      "key": "example-com.html",
      "score": 0.75,
      "text": "# Example Domain\nThis domain is for use in documentation examples..."
    }
  ]
}
```

Log in with your Cloudflare account, then deploy your Worker to make it accessible on the Internet:

```sh
npx wrangler login
npx wrangler deploy
```

## Next steps

[ Browser Run /content endpoint ](https://developers.cloudflare.com/browser-run/quick-actions/content-endpoint/) Fetch the fully rendered HTML of a page after JavaScript runs, with options for load behavior and blocking. 

[ Items Workers binding ](https://developers.cloudflare.com/ai-search/api/items/workers-binding/) Full reference for uploading, listing, and deleting documents in built-in storage. 

[ Website data source ](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) Crawl and index a domain you own automatically, following its sitemap.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/fetch-and-index-web-pages/#page","headline":"Fetch and index single web pages · Cloudflare AI Search docs","description":"Use the Browser Run /content endpoint to fetch a single web page's rendered HTML, then upload it to an AI Search instance's built-in storage so AI Search indexes it for search.","url":"https://developers.cloudflare.com/ai-search/how-to/fetch-and-index-web-pages/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/fetch-and-index-web-pages/","name":"Fetch and index single web pages"}}]}
```

---

---
title: Human-in-the-loop knowledge base updates
description: Build an agent that searches a knowledge base and proposes updates to it, with a human approving and able to roll back each write.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Human-in-the-loop knowledge base updates

This tutorial builds an agent that searches a knowledge base and adds to it, with a human approving every write. Letting an agent modify your data is risky, so each save pauses for approval before it runs, and you can roll back a save that turned out wrong.

## What you will build

A Cloudflare Agent that searches an AI Search instance, proposes new documents to index, waits for you to approve each one, and can undo an approved save.

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

You do not need anything else. The agent provisions its own AI Search instance the first time it runs.

## How it works

The agent uses [Code Mode](https://developers.cloudflare.com/agents/tools/codemode/), a tool-use pattern where the model writes a small program that calls your tools instead of requesting each call separately. A **durable runtime** records every call the program makes, pauses before sensitive calls so a human can approve them, and can compensate applied calls by running a `revert`. That durable state lives in the Agent's Durable Object, so an approval can wait across requests and hibernation.

You expose AI Search to the runtime through a **connector**: a plain class that turns AI Search operations into methods the model can call. This tutorial gives the model a read-only `search` method and a `saveDocument` method that requires approval.

Warning

Code Mode is experimental and may introduce breaking changes. Use caution in production.

## 1\. Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `kb-agent` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- kb-agent
```

```
yarn create cloudflare kb-agent
```

```
pnpm create cloudflare@latest kb-agent
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd kb-agent
```

Install the dependencies. The `ai` and `zod` versions are pinned to the ranges the Agents SDK expects as peer dependencies:

 npm  yarn  pnpm  bun 

```
npm i @cloudflare/codemode @cloudflare/ai-chat agents ai@6 workers-ai-provider zod@4
```

```
yarn add @cloudflare/codemode @cloudflare/ai-chat agents ai@6 workers-ai-provider zod@4
```

```
pnpm add @cloudflare/codemode @cloudflare/ai-chat agents ai@6 workers-ai-provider zod@4
```

```
bun add @cloudflare/codemode @cloudflare/ai-chat agents ai@6 workers-ai-provider zod@4
```

This tutorial uses the AI Search and Worker Loader bindings, which require Wrangler v4\. If `create-cloudflare` set up your project with an earlier version, upgrade it:

 npm  yarn  pnpm  bun 

```
npm i -D wrangler@4
```

```
yarn add -D wrangler@4
```

```
pnpm add -D wrangler@4
```

```
bun add -d wrangler@4
```

## 2\. Configure Wrangler

Replace your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) with the following. This adds the AI Search binding, a Workers AI binding for the model, a Worker Loader binding that runs the model's code in an isolated Worker, and the Durable Object that stores the agent's chat history and durable runtime state.

* [  wrangler.jsonc ](#tab-panel-7225)
* [  wrangler.toml ](#tab-panel-7226)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "kb-agent",
  "main": "src/server.ts",
  // Set this to today's date
  "compatibility_date": "2026-07-14",
  "compatibility_flags": [
    "nodejs_compat"
  ],
  "ai": {
    "binding": "AI"
  },
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ],
  "worker_loaders": [
    {
      "binding": "LOADER"
    }
  ],
  "durable_objects": {
    "bindings": [
      {
        "name": "Chat",
        "class_name": "Chat"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": [
        "Chat"
      ]
    }
  ]
}
```

**TOML**

```toml
name = "kb-agent"
main = "src/server.ts"
# Set this to today's date
compatibility_date = "2026-07-14"
compatibility_flags = ["nodejs_compat"]


[ai]
binding = "AI"


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true


[[worker_loaders]]
binding = "LOADER"


[[durable_objects.bindings]]
name = "Chat"
class_name = "Chat"


[[migrations]]
tag = "v1"
new_sqlite_classes = ["Chat"]
```

AI Search has no local emulator, so the binding always talks to the remote service (`remote = true`). Because of this, you exercise the agent by deploying it rather than with `wrangler dev`. `AIChatAgent` persists messages to SQLite, so its class must be listed in `new_sqlite_classes`.

## 3\. Create the AI Search connector

Create `src/ai-search-connector.ts`. The connector calls the AI Search binding directly, so requests stay in-process and no public endpoint is required.

Give the model a read-only `search` method and a `saveDocument` method. Because `saveDocument` writes content, mark it `requiresApproval` and add a `revert` so the runtime can roll it back.

* [  JavaScript ](#tab-panel-7229)
* [  TypeScript ](#tab-panel-7230)

**src/ai-search-connector.js**

```js
import { CodemodeConnector } from "@cloudflare/codemode";


// The instance this connector reads from and writes to.
const INSTANCE_ID = "knowledge-base";


// A connector turns AI Search operations into methods the model can call from
// its generated code. Each connector becomes one named object in the sandbox.
export class AISearchConnector extends CodemodeConnector {
  // The sandbox global. The model calls `aiSearch.search()` and
  // `aiSearch.saveDocument()`.
  name() {
    return "aiSearch";
  }


  // Shown to the model so it knows what this connector is for.
  instructions() {
    return "Use this connector to search indexed content and save new documents.";
  }


  // Every method the model can call. `inputSchema` is JSON Schema; the runtime
  // validates the model's arguments against it before calling `execute`.
  tools() {
    return {
      // Read-only, so it runs without approval.
      search: {
        description: "Search indexed content and return the matching chunks.",
        inputSchema: {
          type: "object",
          properties: { query: { type: "string" } },
          required: ["query"],
        },
        execute: async (input) => {
          const { query } = input;
          return this.env.AI_SEARCH.get(INSTANCE_ID).search({
            query,
            ai_search_options: { retrieval: { max_num_results: 5 } },
          });
        },
      },
      // Writes content, so it is gated behind approval and made reversible.
      saveDocument: {
        description: "Save a new document to the knowledge base.",
        inputSchema: {
          type: "object",
          properties: {
            name: { type: "string" },
            content: { type: "string" },
          },
          required: ["name", "content"],
        },
        // Pauses the run before this method executes so a human can approve it.
        requiresApproval: true,
        execute: async (input) => {
          const { name, content } = input;
          // upload() queues the document for indexing and returns right away.
          // The item becomes searchable once indexing finishes, a few seconds later.
          const item = await this.env.AI_SEARCH.get(INSTANCE_ID).items.upload(
            name,
            content,
          );
          // This return value is passed to `revert` if the call is rolled back.
          return { id: item.id, key: item.key, status: item.status };
        },
        // Compensating action for rollback: delete the document this call added.
        revert: async (_input, result) => {
          const { id } = result;
          await this.env.AI_SEARCH.get(INSTANCE_ID).items.delete(id);
        },
      },
    };
  }
}
```

**src/ai-search-connector.ts**

```ts
import { CodemodeConnector, type ConnectorTools } from "@cloudflare/codemode";


// The instance this connector reads from and writes to.
const INSTANCE_ID = "knowledge-base";


// A connector turns AI Search operations into methods the model can call from
// its generated code. Each connector becomes one named object in the sandbox.
export class AISearchConnector extends CodemodeConnector<Env> {
  // The sandbox global. The model calls `aiSearch.search()` and
  // `aiSearch.saveDocument()`.
  override name() {
    return "aiSearch";
  }


  // Shown to the model so it knows what this connector is for.
  protected override instructions() {
    return "Use this connector to search indexed content and save new documents.";
  }


  // Every method the model can call. `inputSchema` is JSON Schema; the runtime
  // validates the model's arguments against it before calling `execute`.
  protected override tools(): ConnectorTools {
    return {
      // Read-only, so it runs without approval.
      search: {
        description: "Search indexed content and return the matching chunks.",
        inputSchema: {
          type: "object",
          properties: { query: { type: "string" } },
          required: ["query"],
        },
        execute: async (input) => {
          const { query } = input as { query: string };
          return this.env.AI_SEARCH.get(INSTANCE_ID).search({
            query,
            ai_search_options: { retrieval: { max_num_results: 5 } },
          });
        },
      },
      // Writes content, so it is gated behind approval and made reversible.
      saveDocument: {
        description: "Save a new document to the knowledge base.",
        inputSchema: {
          type: "object",
          properties: {
            name: { type: "string" },
            content: { type: "string" },
          },
          required: ["name", "content"],
        },
        // Pauses the run before this method executes so a human can approve it.
        requiresApproval: true,
        execute: async (input) => {
          const { name, content } = input as { name: string; content: string };
          // upload() queues the document for indexing and returns right away.
          // The item becomes searchable once indexing finishes, a few seconds later.
          const item = await this.env.AI_SEARCH.get(INSTANCE_ID).items.upload(
            name,
            content,
          );
          // This return value is passed to `revert` if the call is rolled back.
          return { id: item.id, key: item.key, status: item.status };
        },
        // Compensating action for rollback: delete the document this call added.
        revert: async (_input, result) => {
          const { id } = result as { id: string };
          await this.env.AI_SEARCH.get(INSTANCE_ID).items.delete(id);
        },
      },
    };
  }
}
```

The `name()` result (`aiSearch`) becomes the global the model's code calls, so the methods are available as `aiSearch.search()` and `aiSearch.saveDocument()`.

## 4\. Build the agent

Create `src/server.ts`. The agent provisions an AI Search instance with [hybrid search](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) enabled the first time it runs, then creates the Code Mode runtime with the connector and exposes it to the model as a single `codemode` tool. The `@callable()` methods let your client list pending approvals and approve, reject, or roll back a write.

* [  JavaScript ](#tab-panel-7231)
* [  TypeScript ](#tab-panel-7232)

**src/server.js**

```js
import { AIChatAgent } from "@cloudflare/ai-chat";
import {
  createCodemodeRuntime,
  DynamicWorkerExecutor,
} from "@cloudflare/codemode";
import { callable, routeAgentRequest } from "agents";
import { createWorkersAI } from "workers-ai-provider";
import { convertToModelMessages, stepCountIs, streamText } from "ai";
import { AISearchConnector } from "./ai-search-connector";


// Code Mode stores its durable state (execution log, pending approvals) in a
// facet exported from the Worker entry. The runtime requires this export.
export { CodemodeRuntime } from "@cloudflare/codemode";


const INSTANCE_ID = "knowledge-base";


// Seed content, so the agent has something to find on the first query.
const SEED_DOC = `# Getting started
AI Search indexes your content so an agent can search it and add to it.`;


export class Chat extends AIChatAgent {
  // In-memory guard, so the one-time setup runs once per instance lifetime.
  ready = false;


  // Create the AI Search instance with hybrid search enabled, then seed it.
  // create() throws if the instance already exists, so the try/catch makes
  // this safe to call on every message.
  async ensureInstance() {
    if (this.ready) return;
    try {
      // index_method with both vector and keyword enables hybrid search.
      await this.env.AI_SEARCH.create({
        id: INSTANCE_ID,
        index_method: { vector: true, keyword: true },
      });
      // Queue the seed document for indexing so the first search has content.
      await this.env.AI_SEARCH.get(INSTANCE_ID).items.upload(
        "getting-started.md",
        SEED_DOC,
      );
    } catch (err) {
      // create() throws if the instance already exists, which is expected on
      // every run after the first. Log anything else so real failures surface.
      console.error("ensureInstance:", err);
    }
    this.ready = true;
  }


  // Build the Code Mode runtime for this request. The handle is cheap to
  // create; the durable state lives in the Durable Object, not the handle.
  #runtime() {
    return createCodemodeRuntime({
      ctx: this.ctx,
      // Runs the model's generated code in an isolated Worker.
      executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),
      connectors: [new AISearchConnector(this.ctx, this.env)],
    });
  }


  // Runs on every chat message from the client.
  async onChatMessage() {
    await this.ensureInstance();


    const workersai = createWorkersAI({ binding: this.env.AI });


    const result = streamText({
      model: workersai("@cf/zai-org/glm-5.2"),
      system:
        "You help maintain a knowledge base. Use codemode to search existing " +
        "content and to save new documents. Search before you answer.",
      messages: await convertToModelMessages(this.messages),
      // The model sees one `codemode` tool and writes code that calls the connector.
      tools: { codemode: this.#runtime().tool() },
      // Cap the agent's tool-use loop.
      stopWhen: stepCountIs(10),
    });


    return result.toUIMessageStreamResponse();
  }


  // The methods below are called from your client to drive the approval flow.


  // List the writes that are paused waiting for approval.
  @callable()
  async pendingApprovals() {
    return this.#runtime().pending();
  }


  // Approve a paused write. The runtime resumes the program and runs it.
  @callable()
  async approveExecution(executionId) {
    return this.#runtime().approve({ executionId });
  }


  // Decline a paused write. The execution ends without saving.
  @callable()
  async rejectExecution(executionId, seq) {
    return this.#runtime().reject({ executionId, seq });
  }


  // Undo an applied write by running the connector's revert.
  @callable()
  async rollbackExecution(executionId) {
    await this.#runtime().rollback({ executionId });
  }
}


export default {
  async fetch(request, env) {
    return (
      (await routeAgentRequest(request, env)) ||
      new Response("Not found", { status: 404 })
    );
  },
};
```

**src/server.ts**

```ts
import { AIChatAgent } from "@cloudflare/ai-chat";
import {
  createCodemodeRuntime,
  DynamicWorkerExecutor,
  type CodemodeRuntimeHandle,
  type PendingAction,
} from "@cloudflare/codemode";
import { callable, routeAgentRequest } from "agents";
import { createWorkersAI } from "workers-ai-provider";
import { convertToModelMessages, stepCountIs, streamText } from "ai";
import { AISearchConnector } from "./ai-search-connector";


// Code Mode stores its durable state (execution log, pending approvals) in a
// facet exported from the Worker entry. The runtime requires this export.
export { CodemodeRuntime } from "@cloudflare/codemode";


const INSTANCE_ID = "knowledge-base";


// Seed content, so the agent has something to find on the first query.
const SEED_DOC = `# Getting started
AI Search indexes your content so an agent can search it and add to it.`;


export class Chat extends AIChatAgent<Env> {
  // In-memory guard, so the one-time setup runs once per instance lifetime.
  private ready = false;


  // Create the AI Search instance with hybrid search enabled, then seed it.
  // create() throws if the instance already exists, so the try/catch makes
  // this safe to call on every message.
  private async ensureInstance() {
    if (this.ready) return;
    try {
      // index_method with both vector and keyword enables hybrid search.
      await this.env.AI_SEARCH.create({
        id: INSTANCE_ID,
        index_method: { vector: true, keyword: true },
      });
      // Queue the seed document for indexing so the first search has content.
      await this.env.AI_SEARCH.get(INSTANCE_ID).items.upload(
        "getting-started.md",
        SEED_DOC,
      );
    } catch (err) {
      // create() throws if the instance already exists, which is expected on
      // every run after the first. Log anything else so real failures surface.
      console.error("ensureInstance:", err);
    }
    this.ready = true;
  }


  // Build the Code Mode runtime for this request. The handle is cheap to
  // create; the durable state lives in the Durable Object, not the handle.
  #runtime(): CodemodeRuntimeHandle {
    return createCodemodeRuntime({
      ctx: this.ctx,
      // Runs the model's generated code in an isolated Worker.
      executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),
      connectors: [new AISearchConnector(this.ctx, this.env)],
    });
  }


  // Runs on every chat message from the client.
  async onChatMessage() {
    await this.ensureInstance();


    const workersai = createWorkersAI({ binding: this.env.AI });


    const result = streamText({
      model: workersai("@cf/zai-org/glm-5.2"),
      system:
        "You help maintain a knowledge base. Use codemode to search existing " +
        "content and to save new documents. Search before you answer.",
      messages: await convertToModelMessages(this.messages),
      // The model sees one `codemode` tool and writes code that calls the connector.
      tools: { codemode: this.#runtime().tool() },
      // Cap the agent's tool-use loop.
      stopWhen: stepCountIs(10),
    });


    return result.toUIMessageStreamResponse();
  }


  // The methods below are called from your client to drive the approval flow.


  // List the writes that are paused waiting for approval.
  @callable()
  async pendingApprovals(): Promise<PendingAction[]> {
    return this.#runtime().pending();
  }


  // Approve a paused write. The runtime resumes the program and runs it.
  @callable()
  async approveExecution(executionId: string) {
    return this.#runtime().approve({ executionId });
  }


  // Decline a paused write. The execution ends without saving.
  @callable()
  async rejectExecution(executionId: string, seq: number): Promise<boolean> {
    return this.#runtime().reject({ executionId, seq });
  }


  // Undo an applied write by running the connector's revert.
  @callable()
  async rollbackExecution(executionId: string): Promise<void> {
    await this.#runtime().rollback({ executionId });
  }
}


export default {
  async fetch(request: Request, env: Env) {
    return (
      (await routeAgentRequest(request, env)) ||
      new Response("Not found", { status: 404 })
    );
  },
} satisfies ExportedHandler<Env>;
```

Generate types:

```sh
npx wrangler types
```

## 5\. Deploy

Because AI Search runs remotely, you deploy the Worker to run the agent.

Log in with your Cloudflare account:

```sh
npx wrangler login
```

Deploy your Worker to make it accessible on the Internet:

```sh
npx wrangler deploy
```

Wrangler prints your Worker's URL, for example `https://kb-agent.<your-subdomain>.workers.dev`. You use it in the next step.

## 6\. Try the approval and rollback flow

The model receives one `codemode` tool. When you ask it to find and save content, it writes a short program that calls the connector methods:

**JavaScript**

```js
// The model writes this program. It runs inside the Code Mode sandbox.
async () => {
  // The read-only search runs immediately.
  const existing = await aiSearch.search({ query: "onboarding steps" });


  // Only save when the knowledge base has no matching content yet.
  if (existing.chunks.length === 0) {
    // saveDocument requires approval, so this call pauses the program here.
    await aiSearch.saveDocument({
      name: "onboarding.md",
      content: "# Onboarding\nStep 1: create an account.",
    });
  }


  return existing.chunks.length;
};
```

`aiSearch.search()` runs immediately. When the program reaches `aiSearch.saveDocument()`, the runtime records the call as pending and pauses the execution before the upload runs.

Your client sends the chat message that starts the run, then drives the approval with the `@callable()` methods. The following script uses the [Agents SDK client](https://developers.cloudflare.com/agents/communication-channels/chat/client-sdk/) to do both. Save it as `client.mjs`, set `HOST` to your deployed Worker, and run it with `node client.mjs`:

* [  JavaScript ](#tab-panel-7227)
* [  TypeScript ](#tab-panel-7228)

**client.mjs**

```js
import { AgentClient } from "agents/client";


// Your deployed Worker, without the protocol.
const HOST = "kb-agent.<your-subdomain>.workers.dev";


const client = new AgentClient({ agent: "Chat", name: "default", host: HOST });
await client.ready;


// 1. Ask the agent to find and save content. It writes a Code Mode program;
//    the saveDocument call pauses for approval instead of running.
client.send(
  JSON.stringify({
    type: "cf_agent_use_chat_request",
    id: crypto.randomUUID(),
    init: {
      method: "POST",
      body: JSON.stringify({
        messages: [
          {
            id: crypto.randomUUID(),
            role: "user",
            parts: [
              {
                type: "text",
                text: "Search for onboarding steps. If there is none, save a document named onboarding.md with a short onboarding guide.",
              },
            ],
          },
        ],
      }),
    },
  }),
);


// Give the turn time to run and pause at saveDocument.
await new Promise((r) => setTimeout(r, 20_000));


// 2. List the writes waiting for approval.
const pending = await client.call("pendingApprovals");
console.log("Pending approvals:", pending);


// 3. Approve the first one. The runtime replays the program: completed calls
//    return their recorded results, and the approved saveDocument runs.
if (pending.length > 0) {
  await client.call("approveExecution", [pending[0].executionId]);
  console.log("Approved", pending[0].executionId);


  // To roll back later, delete the uploaded document:
  // await client.call("rollbackExecution", [pending[0].executionId]);
}


client.close();
```

**client.mjs**

```ts
import { AgentClient } from "agents/client";


// Your deployed Worker, without the protocol.
const HOST = "kb-agent.<your-subdomain>.workers.dev";


const client = new AgentClient({ agent: "Chat", name: "default", host: HOST });
await client.ready;


// 1. Ask the agent to find and save content. It writes a Code Mode program;
//    the saveDocument call pauses for approval instead of running.
client.send(
  JSON.stringify({
    type: "cf_agent_use_chat_request",
    id: crypto.randomUUID(),
    init: {
      method: "POST",
      body: JSON.stringify({
        messages: [
          {
            id: crypto.randomUUID(),
            role: "user",
            parts: [
              {
                type: "text",
                text: "Search for onboarding steps. If there is none, save a document named onboarding.md with a short onboarding guide.",
              },
            ],
          },
        ],
      }),
    },
  }),
);


// Give the turn time to run and pause at saveDocument.
await new Promise((r) => setTimeout(r, 20_000));


// 2. List the writes waiting for approval.
const pending = await client.call("pendingApprovals");
console.log("Pending approvals:", pending);


// 3. Approve the first one. The runtime replays the program: completed calls
//    return their recorded results, and the approved saveDocument runs.
if (pending.length > 0) {
  await client.call("approveExecution", [pending[0].executionId]);
  console.log("Approved", pending[0].executionId);


  // To roll back later, delete the uploaded document:
  // await client.call("rollbackExecution", [pending[0].executionId]);
}


client.close();
```

Each `PendingAction` from `pendingApprovals()` includes the `executionId`, a `seq` number, and the method and arguments, so you can show the pending document to the user before deciding. The approval methods behave as follows:

* `approveExecution(executionId)` replays the program and runs the approved `saveDocument`. The document is queued for indexing and becomes searchable a few seconds later.
* `rejectExecution(executionId, seq)` ends the execution without saving.
* `rollbackExecution(executionId)` undoes an applied write by running the connector's `revert`, which deletes the uploaded document.

## What you built

Your agent can now:

* Search the knowledge base with a read-only tool.
* Propose new documents through a write tool that pauses for human approval.
* Resume the same program after approval, without re-running completed work.
* Roll back an approved save by deleting the indexed document.

## Next steps

[ Create a durable Code Mode runtime ](https://developers.cloudflare.com/agents/tools/codemode/durable-runtime/) The full runtime API: rejection, execution history, and reusable snippets. 

[ AI Search as an agent tool ](https://developers.cloudflare.com/agents/tools/ai-search/) Give a Cloudflare Agent retrieval with AI Search. 

[ Hybrid search ](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) Combine vector and keyword search with configurable fusion. 

[ Items Workers binding ](https://developers.cloudflare.com/ai-search/api/items/workers-binding/) Full reference for uploading, listing, and deleting documents.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/human-in-the-loop-knowledge-base/#page","headline":"Human-in-the-loop knowledge base updates · Cloudflare AI Search docs","description":"Build an agent that searches a knowledge base and proposes updates to it, with a human approving and able to roll back each write.","url":"https://developers.cloudflare.com/ai-search/how-to/human-in-the-loop-knowledge-base/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/human-in-the-loop-knowledge-base/","name":"Human-in-the-loop knowledge base updates"}}]}
```

---

---
title: NLWeb
description: Deploy NLWeb with AI Search to enable conversational natural language queries on your website.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# NLWeb

Enable conversational search on your website with NLWeb and Cloudflare AI Search. This template crawls your site, indexes the content, and deploys NLWeb-standard endpoints to serve both people and AI agents.

Note

This is a public preview ideal for experimentation. If you're interested in running this in production workflows, please contact us at [nlweb@cloudflare.com](mailto:nlweb@cloudflare.com).

## What is NLWeb

[NLWeb ↗](https://github.com/nlweb-ai/NLWeb) is an open project developed by Microsoft that defines a standard protocol for natural language queries on websites. Its goal is to make every website as accessible and interactive as a conversational AI app, so both people and AI agents can reliably query site content. It does this by exposing two key endpoints:

* `/ask`: Conversational endpoint for user queries
* `/mcp`: Structured Model Context Protocol (MCP) endpoint for AI agents

## How to use it

You can deploy NLWeb on your website directly through the AI Search dashboard:

1. Log in to your [Cloudflare dashboard ↗](https://dash.cloudflare.com/).
2. Go to **AI** \> **AI Search**.  
[ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
3. Select **Create AI Search**.
4. Select **Website** as a data source.
5. Follow the instructions to create an AI Search instance.
6. Go to the **Settings** for the instance
7. Find **NLWeb Worker** and select "Enable AI Search for your website".

Once complete, AI Search will deploy an NLWeb Worker for you that enables you to use the NLWeb API endpoints.

## What this template includes

Choosing the NLWeb Website option extends a normal AI Search by tailoring it for content‑heavy websites and giving you everything that is required to adopt NLWeb as the standard for conversational search on your site. Specifically, the template provides:

* **Website as a data source:** Uses [Website](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) as data source option to crawl and ingest pages with the Rendered Sites option.
* **Defaults for content-heavy websites:** Applies tuned embedding and retrieval configurations ideal for publishing and content‑rich websites.
* **NLWeb Worker deployment:** Automatically spins up a Cloudflare Worker from the [NLWeb Worker template ↗](https://github.com/cloudflare/templates).

## What the Worker includes

Your deployed Worker provides two endpoints:

* `/ask`: NLWeb’s standard conversational endpoint  
  * Powers the conversational UI at the root (`/`)
  * Powers the embeddable preview widget (`/snippet.html`)
* `/mcp`: NLWeb’s MCP server endpoint for trusted AI agents

These endpoints give both people and agents structured access to your content.

## Using it on your website

You can use the embeddable snippet to add a search UI directly into your website. For example:

```html
<!-- Add css on head -->
<link rel="stylesheet" href="https://ask.example.com/nlweb-dropdown-chat.css" />
<link rel="stylesheet" href="https://ask.example.com/common-chat-styles.css" />


<!-- Add container on body -->
<div id="docs-search-container"></div>


<!-- Include JavaScript -->
<script type="module">
  import { NLWebDropdownChat } from "https://ask.example.com/nlweb-dropdown-chat.js";


  const chat = new NLWebDropdownChat({
    containerId: "docs-search-container",
    site: "https://ask.example.com",
    placeholder: "Search for docs...",
    endpoint: "https://ask.example.com",
  });
</script>
```

This lets you serve conversational AI search directly from your own domain, with control over how people and agents access your content.

## Modifying or updating the Worker

You may want to customize your Worker, for example, to adjust the UI for the embeddable snippet. In those cases, we recommend calling the `/ask` endpoint for queries, and building your own UI on top of it. However, you may also choose to modify the Worker's code for the embeddable UI.

If the NLWeb standard is updated, you can update your Worker to stay compatible and receive the latest updates.

The simplest way to apply changes or updates is to redeploy the Worker template:

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/nlweb-template)

1. Use the **Deploy to Cloudflare** button from above to deploy the Worker template to your Cloudflare account.
2. Enter the name of your AI Search in the `RAG_ID` environment variable field.
3. Click **Deploy**.
4. Select the **GitHub/GitLab** icon on the Workers Dashboard.
5. Clone the repository that is created for your Worker.
6. Make your modifications, then commit and push changes to the repository to update your Worker.

Now you can use this Worker as the new NLWeb endpoint for your website.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/nlweb/#page","headline":"NLWeb · Cloudflare AI Search docs","description":"Deploy NLWeb with AI Search to enable conversational natural language queries on your website.","url":"https://developers.cloudflare.com/ai-search/how-to/nlweb/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/nlweb/","name":"NLWeb"}}]}
```

---

---
title: Multitenancy
description: Keep each tenant's data isolated in AI Search using a separate instance per tenant or a shared instance with metadata filtering.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Multitenancy

In a multi-tenant application, each tenant must only ever see their own data. AI Search supports two ways to isolate search per tenant: give each tenant its own instance, or share one instance and filter by tenant at query time.

## Choose an approach

| Approach                                                                                         | How it isolates                                                      | Choose it when                                                         |
| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| [Instance per tenant](#option-1-one-instance-per-tenant) (recommended)                           | Each tenant gets a separate instance with its own storage and index  | You need strong isolation, or you create and delete tenants at runtime |
| [Shared instance with filtering](#option-2-shared-instance-with-metadata-filtering-on-retrieval) | One instance holds every tenant; a metadata filter scopes each query | You have many small tenants and want the simplest setup                |

## Prerequisites

Both approaches use a Cloudflare Worker. Create the project first, then follow the option you chose.

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

## Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `tenant-search` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- tenant-search
```

```
yarn create cloudflare tenant-search
```

```
pnpm create cloudflare@latest tenant-search
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd tenant-search
```

## Option 1: One instance per tenant

This is the **recommended** approach. Each tenant gets a separate instance with its own storage and search index, so one tenant can never retrieve another tenant's documents.

Create an isolated AI Search instance for each tenant at runtime using the [namespace binding](https://developers.cloudflare.com/ai-search/concepts/namespaces/).

[ Tenant A ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ Tenant B ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ Tenant C ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) 

[ env.AI\_SEARCH.get(id) Worker ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) 

namespace: tenants 

[ AI Search instance tenant-a ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ AI Search instance tenant-b ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ AI Search instance tenant-c ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) 

Note

AI Search limits the number of [instances per account](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits). If you have more tenants than that limit, or run into any other AI Search limit, reach out through the [Limit Increase Request Form ↗](https://forms.gle/wnizxrEUW33Y15CT8) and we can help.

Add the namespace binding to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/):

* [  wrangler.jsonc ](#tab-panel-7233)
* [  wrangler.toml ](#tab-panel-7234)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "TENANTS",
      "namespace": "default",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
[[ai_search_namespaces]]
binding = "TENANTS"
namespace = "default"
remote = true
```

The `remote` option lets `wrangler dev` proxy requests to your deployed instances, since AI Search does not run locally.

### Built-in storage

Each tenant's instance holds documents that you upload directly to it, with no external data source.

Update `src/index.ts`. This Worker identifies the tenant from a request header, then creates, populates, searches, and deletes that tenant's instance.

* [  JavaScript ](#tab-panel-7239)
* [  TypeScript ](#tab-panel-7240)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);


    // Identify the tenant from the request header.
    const tenantId = request.headers.get("x-tenant-id");


    if (!tenantId) {
      return new Response("Missing x-tenant-id header", { status: 400 });
    }


    // Create a new instance for the tenant.
    if (url.pathname === "/onboard" && request.method === "POST") {
      const instance = await env.TENANTS.create({
        id: `tenant-${tenantId}`,
      });
      return Response.json({ success: true, instance: await instance.info() });
    }


    // Upload a document to the tenant's instance.
    if (url.pathname === "/upload" && request.method === "POST") {
      const formData = await request.formData();
      const file = formData.get("file");


      const item = await env.TENANTS.get(`tenant-${tenantId}`).items.upload(
        file.name,
        await file.arrayBuffer(),
      );
      return Response.json({ success: true, item });
    }


    // Search the tenant's instance. Search is isolated to their instance.
    if (url.pathname === "/search") {
      const query = url.searchParams.get("q") || "";


      const results = await env.TENANTS.get(`tenant-${tenantId}`).search({
        messages: [{ role: "user", content: query }],
      });
      return Response.json(results);
    }


    // Delete the tenant's instance and all its data.
    if (url.pathname === "/offboard" && request.method === "DELETE") {
      await env.TENANTS.delete(`tenant-${tenantId}`);
      return Response.json({ success: true });
    }


    return new Response("Not found", { status: 404 });
  },
};
```

**src/index.ts**

```ts
export type Env = {
  TENANTS: AiSearchNamespace;
};


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);


    // Identify the tenant from the request header.
    const tenantId = request.headers.get("x-tenant-id");


    if (!tenantId) {
      return new Response("Missing x-tenant-id header", { status: 400 });
    }


    // Create a new instance for the tenant.
    if (url.pathname === "/onboard" && request.method === "POST") {
      const instance = await env.TENANTS.create({
        id: `tenant-${tenantId}`,
      });
      return Response.json({ success: true, instance: await instance.info() });
    }


    // Upload a document to the tenant's instance.
    if (url.pathname === "/upload" && request.method === "POST") {
      const formData = await request.formData();
      const file = formData.get("file") as File;


      const item = await env.TENANTS.get(`tenant-${tenantId}`).items.upload(
        file.name,
        await file.arrayBuffer(),
      );
      return Response.json({ success: true, item });
    }


    // Search the tenant's instance. Search is isolated to their instance.
    if (url.pathname === "/search") {
      const query = url.searchParams.get("q") || "";


      const results = await env.TENANTS.get(`tenant-${tenantId}`).search({
        messages: [{ role: "user", content: query }],
      });
      return Response.json(results);
    }


    // Delete the tenant's instance and all its data.
    if (url.pathname === "/offboard" && request.method === "DELETE") {
      await env.TENANTS.delete(`tenant-${tenantId}`);
      return Response.json({ success: true });
    }


    return new Response("Not found", { status: 404 });
  },
} satisfies ExportedHandler<Env>;
```

Start a local development server:

```sh
npx wrangler dev
```

Then onboard a tenant, upload a document to their instance, search it, and offboard. The `x-tenant-id` header scopes every request to that tenant's instance:

```sh
# Create an isolated instance for tenant "acme"
curl -X POST http://localhost:8787/onboard -H "x-tenant-id: acme"


# Upload a document to acme's instance
curl -X POST http://localhost:8787/upload -H "x-tenant-id: acme" -F "file=@./handbook.pdf"


# Search acme's instance
curl "http://localhost:8787/search?q=vacation+policy" -H "x-tenant-id: acme"


# Delete acme's instance and all of its data
curl -X DELETE http://localhost:8787/offboard -H "x-tenant-id: acme"
```

AI Search indexes uploads asynchronously, so wait a moment after uploading before you search.

### R2

If your tenants' data already lives in [R2](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/), back each tenant's instance with R2 instead of uploading documents to built-in storage. Change the `/onboard` route from the [built-in storage](#built-in-storage) example to create an R2-backed instance. How you configure it depends on how the data is organized.

Note

Creating an `r2`\-backed instance from a binding requires a [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/). Cloudflare registers one for your account automatically the first time you create an R2-backed instance through the [dashboard](https://developers.cloudflare.com/ai-search/get-started/dashboard/) or [Wrangler](https://developers.cloudflare.com/ai-search/get-started/wrangler/), and reuses it afterward.

**A bucket per tenant:** if each tenant's data is already in its own bucket, point the instance at that whole bucket:

**TypeScript**

```ts
const instance = await env.TENANTS.create({
  id: `tenant-${tenantId}`,
  type: "r2",
  source: `tenant-${tenantId}-bucket`,
});
```

**A shared bucket:** if every tenant's data lives in one bucket, organized by folder:

* Directorymy-bucket  
  * Directorycustomers/  
    * Directoryacme/
      * …
    * Directoryglobex/
      * …

Scope each instance to that tenant's folder with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/), so it only ever indexes and searches that tenant's objects:

**TypeScript**

```ts
const instance = await env.TENANTS.create({
  id: `tenant-${tenantId}`,
  type: "r2",
  source: "my-bucket",
  source_params: {
    include_items: [`/customers/${tenantId}/**`],
  },
});
```

With either layout, AI Search indexes each tenant's objects on the next [sync](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/). Add documents by writing them to R2 rather than uploading through the Worker, and keep the search and offboard routes from the built-in storage example: each instance only ever returns its own tenant's results, and deleting an instance removes its search index while leaving the R2 objects untouched.

To try it, run `npx wrangler dev` and use the same `onboard`, `search`, and `offboard` requests as [built-in storage](#built-in-storage). Skip the upload step, since AI Search indexes each tenant's objects directly from R2.

## Option 2: Shared instance with metadata filtering on retrieval

Use a single AI Search instance and organize content by tenant using folder paths. This approach works with both [R2 buckets](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/) and [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/). Apply [metadata filters](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) at query time so each tenant only retrieves their own documents.

This option searches an existing instance, so create one named `shared-instance` and add your content first. Refer to [Get started](https://developers.cloudflare.com/ai-search/get-started/).

Add the instance binding to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/):

* [  wrangler.jsonc ](#tab-panel-7235)
* [  wrangler.toml ](#tab-panel-7236)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search": [
    {
      "binding": "SHARED_INSTANCE",
      "instance_name": "shared-instance",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
[[ai_search]]
binding = "SHARED_INSTANCE"
instance_name = "shared-instance"
remote = true
```

Organize your content by tenant using unique folder paths:

* Directorycustomer-a  
  * Directorylogs/
    * …
  * Directorycontracts/
    * …
* Directorycustomer-b  
  * Directorycontracts/
    * …

Update `src/index.ts` to filter by the tenant's folder at query time:

* [  JavaScript ](#tab-panel-7237)
* [  TypeScript ](#tab-panel-7238)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const tenantId = request.headers.get("x-tenant-id");


    if (!tenantId) {
      return new Response("Missing x-tenant-id header", { status: 400 });
    }


    // Filter results to only return documents from this tenant's folder.
    const results = await env.SHARED_INSTANCE.search({
      messages: [{ role: "user", content: "When did I sign my agreement?" }],
      ai_search_options: {
        retrieval: {
          filters: {
            folder: { $gte: `${tenantId}/`, $lt: `${tenantId}0` },
          },
        },
      },
    });


    return Response.json(results);
  },
};
```

**src/index.ts**

```ts
export type Env = {
  SHARED_INSTANCE: AiSearchInstance;
};


export default {
  async fetch(request, env): Promise<Response> {
    const tenantId = request.headers.get("x-tenant-id");


    if (!tenantId) {
      return new Response("Missing x-tenant-id header", { status: 400 });
    }


    // Filter results to only return documents from this tenant's folder.
    const results = await env.SHARED_INSTANCE.search({
      messages: [{ role: "user", content: "When did I sign my agreement?" }],
      ai_search_options: {
        retrieval: {
          filters: {
            folder: { $gte: `${tenantId}/`, $lt: `${tenantId}0` },
          },
        },
      },
    });


    return Response.json(results);
  },
} satisfies ExportedHandler<Env>;
```

This example uses a ["starts with" filter](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/#starts-with-filter-for-folders) to match all files under the tenant's folder, including subfolders.

Start a local development server:

```sh
npx wrangler dev
```

Send a request as each tenant. The Worker scopes the search to that tenant's folder, so the results never overlap:

```sh
curl http://localhost:8787/ -H "x-tenant-id: customer-a"
curl http://localhost:8787/ -H "x-tenant-id: customer-b"
```

## Deploy

Log in with your Cloudflare account, then deploy your Worker to make it accessible on the Internet:

```sh
npx wrangler login
npx wrangler deploy
```

## Next steps

[ Namespaces ](https://developers.cloudflare.com/ai-search/concepts/namespaces/) Group instances and manage them dynamically from a binding. 

[ Filtering ](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) Filter search results by metadata attributes at query time.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/#page","headline":"Multitenancy · Cloudflare AI Search docs","description":"Keep each tenant's data isolated in AI Search using a separate instance per tenant or a shared instance with metadata filtering.","url":"https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/per-tenant-search/","name":"Multitenancy"}}]}
```

---

---
title: Search across multiple instances
description: Search a shared knowledge base and a tenant-specific one in a single query, and identify which instance each result came from.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Search across multiple instances

AI Search can query several instances in one request, merge the results, and tag each result with the instance it came from. This guide uses that to search two knowledge bases together: a shared **general** knowledge base that every user can search, plus a **tenant-specific** knowledge base that holds one customer's private content. A single query returns relevant content from both.

Keeping each tenant's content in its own instance is the recommended way to isolate tenants (refer to [Multi-tenant search isolation](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/)). Searching across instances then lets you combine a tenant's instance with shared content at query time, so you do not have to copy the shared content into every tenant's instance.

**What you will build:** A Worker that searches a shared `general-knowledge` instance and a per-tenant instance together, then groups the results by their source.

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

The instances you search across must belong to the same [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/). This guide creates them for you.

## 1\. Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `multi-source-search` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- multi-source-search
```

```
yarn create cloudflare multi-source-search
```

```
pnpm create cloudflare@latest multi-source-search
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd multi-source-search
```

## 2\. Configure Wrangler

Add an [AI Search namespace binding](https://developers.cloudflare.com/ai-search/concepts/namespaces/) to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). Searching across instances is a method on the namespace binding, so a single binding can reach every instance in the namespace.

* [  wrangler.jsonc ](#tab-panel-7241)
* [  wrangler.toml ](#tab-panel-7242)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "multi-source-search",
  "main": "src/index.ts",
  // Set this to today's date
  "compatibility_date": "2026-07-14",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
name = "multi-source-search"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-07-14"


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true
```

The `remote` option lets `wrangler dev` proxy requests to your deployed instances, since AI Search does not run locally.

## 3\. Add the Worker code

Update `src/index.ts`. This Worker identifies the tenant from a request header, then searches the shared instance and that tenant's instance in one call. Each returned chunk carries an `instance_id`, so the Worker can group results by source.

* [  JavaScript ](#tab-panel-7243)
* [  TypeScript ](#tab-panel-7244)

**src/index.js**

```js
// The shared instance that every tenant can search.
const GENERAL_INSTANCE = "general-knowledge";
// Each tenant also has its own instance, holding only that tenant's content.
const tenantInstance = (tenantId) => `tenant-${tenantId}`;


// Seed content, so each instance returns something on the first query. In a
// real app you would provision instances and their content ahead of time.
const GENERAL_DOC = `# Support hours
Support is available Monday to Friday, 9am to 5pm UTC for all customers.`;
const TENANT_DOC = `# Your plan
This account is on the Enterprise plan with a dedicated success manager.`;


export default {
  async fetch(request, env) {
    // Identify the tenant and read the query.
    const tenantId = request.headers.get("x-tenant-id");
    if (!tenantId) {
      return new Response("Missing x-tenant-id header", { status: 400 });
    }
    const query = new URL(request.url).searchParams.get("q");
    if (!query) {
      return new Response("Add a ?q= query parameter", { status: 400 });
    }


    // One-time setup for this demo: create both instances and seed each.
    await ensureInstance(
      env,
      GENERAL_INSTANCE,
      "support-hours.md",
      GENERAL_DOC,
    );
    await ensureInstance(env, tenantInstance(tenantId), "plan.md", TENANT_DOC);


    // Search the shared instance and this tenant's instance in one call.
    // AI Search fans out to both, merges the results, and tags each chunk
    // with the instance_id it came from. You can pass 1 to 10 instance IDs.
    const results = await env.AI_SEARCH.search({
      query,
      ai_search_options: {
        instance_ids: [GENERAL_INSTANCE, tenantInstance(tenantId)],
      },
    });


    // Use the instance_id tag to separate shared results from tenant results.
    const fromGeneral = results.chunks.filter(
      (chunk) => chunk.instance_id === GENERAL_INSTANCE,
    );
    const fromTenant = results.chunks.filter(
      (chunk) => chunk.instance_id === tenantInstance(tenantId),
    );


    return Response.json({
      query: results.search_query,
      general: fromGeneral.map((chunk) => ({
        key: chunk.item.key,
        text: chunk.text,
      })),
      tenant: fromTenant.map((chunk) => ({
        key: chunk.item.key,
        text: chunk.text,
      })),
      // If one instance fails, the other still returns. Failures land here.
      errors: results.errors,
    });
  },
};


// Create an instance with built-in storage and seed one document. Safe to call
// on every request: create() throws if the instance already exists, so the
// try/catch skips setup once it is in place.
async function ensureInstance(env, id, key, content) {
  try {
    await env.AI_SEARCH.create({ id });
    await env.AI_SEARCH.get(id).items.uploadAndPoll(key, content, {
      timeoutMs: 60_000,
    });
  } catch {
    // The instance already exists and has been seeded.
  }
}
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


// The shared instance that every tenant can search.
const GENERAL_INSTANCE = "general-knowledge";
// Each tenant also has its own instance, holding only that tenant's content.
const tenantInstance = (tenantId: string) => `tenant-${tenantId}`;


// Seed content, so each instance returns something on the first query. In a
// real app you would provision instances and their content ahead of time.
const GENERAL_DOC = `# Support hours
Support is available Monday to Friday, 9am to 5pm UTC for all customers.`;
const TENANT_DOC = `# Your plan
This account is on the Enterprise plan with a dedicated success manager.`;


export default {
  async fetch(request, env): Promise<Response> {
    // Identify the tenant and read the query.
    const tenantId = request.headers.get("x-tenant-id");
    if (!tenantId) {
      return new Response("Missing x-tenant-id header", { status: 400 });
    }
    const query = new URL(request.url).searchParams.get("q");
    if (!query) {
      return new Response("Add a ?q= query parameter", { status: 400 });
    }


    // One-time setup for this demo: create both instances and seed each.
    await ensureInstance(
      env,
      GENERAL_INSTANCE,
      "support-hours.md",
      GENERAL_DOC,
    );
    await ensureInstance(env, tenantInstance(tenantId), "plan.md", TENANT_DOC);


    // Search the shared instance and this tenant's instance in one call.
    // AI Search fans out to both, merges the results, and tags each chunk
    // with the instance_id it came from. You can pass 1 to 10 instance IDs.
    const results = await env.AI_SEARCH.search({
      query,
      ai_search_options: {
        instance_ids: [GENERAL_INSTANCE, tenantInstance(tenantId)],
      },
    });


    // Use the instance_id tag to separate shared results from tenant results.
    const fromGeneral = results.chunks.filter(
      (chunk) => chunk.instance_id === GENERAL_INSTANCE,
    );
    const fromTenant = results.chunks.filter(
      (chunk) => chunk.instance_id === tenantInstance(tenantId),
    );


    return Response.json({
      query: results.search_query,
      general: fromGeneral.map((chunk) => ({
        key: chunk.item.key,
        text: chunk.text,
      })),
      tenant: fromTenant.map((chunk) => ({
        key: chunk.item.key,
        text: chunk.text,
      })),
      // If one instance fails, the other still returns. Failures land here.
      errors: results.errors,
    });
  },
} satisfies ExportedHandler<Env>;


// Create an instance with built-in storage and seed one document. Safe to call
// on every request: create() throws if the instance already exists, so the
// try/catch skips setup once it is in place.
async function ensureInstance(
  env: Env,
  id: string,
  key: string,
  content: string,
) {
  try {
    await env.AI_SEARCH.create({ id });
    await env.AI_SEARCH.get(id).items.uploadAndPoll(key, content, {
      timeoutMs: 60_000,
    });
  } catch {
    // The instance already exists and has been seeded.
  }
}
```

`env.AI_SEARCH.search()` is the namespace-level search. It differs from `env.AI_SEARCH.get(id).search()`, which searches a single instance. Passing `instance_ids` fans the query out across those instances and returns one merged, ranked list of chunks. Because every chunk includes an `instance_id`, you always know whether a result came from the shared instance or the tenant's instance.

If one instance fails, for example because the ID does not exist, the others still return, and the failure is reported in `errors` instead of throwing. This makes a missing tenant instance a partial result rather than a hard error.

## 4\. Run it

Start a local development server:

```sh
npx wrangler dev
```

Send a request with a tenant header and a query. The first request takes a moment because it creates and seeds the instances:

```sh
curl "http://localhost:8787/?q=support+hours+and+my+plan" -H "x-tenant-id: acme"
```

The response separates results from the shared instance and the tenant instance:

```json
{
  "query": "support hours and my plan",
  "general": [
    {
      "key": "support-hours.md",
      "text": "# Support hours\nSupport is available..."
    }
  ],
  "tenant": [
    {
      "key": "plan.md",
      "text": "# Your plan\nThis account is on the Enterprise plan..."
    }
  ]
}
```

When every instance succeeds, the response has no `errors` field. If an instance fails, an `errors` array lists the failures while the other instances' results are still returned.

## 5\. Generate an answer over both instances

To return a single written answer grounded in both instances instead of raw chunks, use `chatCompletions` with the same `instance_ids`. It retrieves from every listed instance, then generates one response from the combined context:

**TypeScript**

```ts
const completion = await env.AI_SEARCH.chatCompletions({
  query,
  ai_search_options: {
    instance_ids: [GENERAL_INSTANCE, tenantInstance(tenantId)],
  },
});


// The generated answer, grounded in both the shared and tenant content.
const answer = completion.choices[0]?.message.content;
```

The response also includes the retrieved `chunks` (each tagged with its `instance_id`) so you can cite sources, and an `errors` array for any instance that failed.

## 6\. Deploy

Log in with your Cloudflare account, then deploy your Worker:

```sh
npx wrangler login
npx wrangler deploy
```

## Next steps

[ Multi-tenant search isolation ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) Give each tenant its own instance, or share one instance with metadata filtering. 

[ Namespaces ](https://developers.cloudflare.com/ai-search/concepts/namespaces/) How instances are grouped, and how the namespace binding addresses them. 

[ Search Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Full reference for single-instance and multi-instance search and chat.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/search-multiple-sources/#page","headline":"Search across multiple instances · Cloudflare AI Search docs","description":"Search a shared knowledge base and a tenant-specific one in a single query, and identify which instance each result came from.","url":"https://developers.cloudflare.com/ai-search/how-to/search-multiple-sources/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/search-multiple-sources/","name":"Search across multiple instances"}}]}
```

---

---
title: Create a simple search engine
description: Build a simple search engine using the AI Search Workers binding and the search method.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Create a simple search engine

This guide builds a search engine that returns the file names matching a query, using the `search()` method on the [Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/). You can adapt it to use the [REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/) instead.

For the best results with this pattern:

* Disable query rewriting so the original user query is matched directly.
* Configure your AI Search instance with small chunk sizes (256 tokens is usually enough).

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

You also need an AI Search instance that already contains indexed content. To create one and add content, refer to [Get started](https://developers.cloudflare.com/ai-search/get-started/).

## 1\. Create a Worker project

Create a new Worker project using the `create-cloudflare` CLI (C3). [C3 ↗](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named `search-engine` by running:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- search-engine
```

```
yarn create cloudflare search-engine
```

```
pnpm create cloudflare@latest search-engine
```

For setup, select the following options:

* For _What would you like to start with?_, choose `Hello World example`.
* For _Which template would you like to use?_, choose `Worker only`.
* For _Which language do you want to use?_, choose `TypeScript`.
* For _Do you want to use git for version control?_, choose `Yes`.
* For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

Go to your application directory:

```sh
cd search-engine
```

## 2\. Bind your Worker to AI Search

Add the following to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/):

* [  wrangler.jsonc ](#tab-panel-7245)
* [  wrangler.toml ](#tab-panel-7246)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
```

**TOML**

```toml
[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true
```

This binds the `default` [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) to `env.AI_SEARCH`. The `remote` option lets `wrangler dev` proxy requests to your deployed instance, since AI Search does not run locally.

## 3\. Add the search code

Update `src/index.ts`. This Worker reads a query from the URL, searches your instance, and returns the file name of each matching chunk. Replace `my-instance` with the name of your instance.

* [  JavaScript ](#tab-panel-7247)
* [  TypeScript ](#tab-panel-7248)

**src/index.js**

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const userQuery = url.searchParams.get("query") ?? "What is Cloudflare?";


    const searchResult = await env.AI_SEARCH.get("my-instance").search({
      messages: [{ role: "user", content: userQuery }],
    });


    return Response.json({
      files: searchResult.chunks.map((chunk) => chunk.item.key),
    });
  },
};
```

**src/index.ts**

```ts
export interface Env {
  AI_SEARCH: AiSearchNamespace;
}


export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    const userQuery = url.searchParams.get("query") ?? "What is Cloudflare?";


    const searchResult = await env.AI_SEARCH.get("my-instance").search({
      messages: [{ role: "user", content: userQuery }],
    });


    return Response.json({
      files: searchResult.chunks.map((chunk) => chunk.item.key),
    });
  },
} satisfies ExportedHandler<Env>;
```

## 4\. Run and deploy

Start a local development server, then query it at `/?query=your+search+terms`:

```sh
npx wrangler dev
```

Log in with your Cloudflare account, then deploy your Worker to make it accessible on the Internet:

```sh
npx wrangler login
npx wrangler deploy
```

## Next steps

[ Search Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Full reference for searching and chatting from a Worker. 

[ Query rewriting ](https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/) Control whether AI Search rewrites the query before searching.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/simple-search-engine/#page","headline":"Create a simple search engine · Cloudflare AI Search docs","description":"Build a simple search engine using the AI Search Workers binding and the search method.","url":"https://developers.cloudflare.com/ai-search/how-to/simple-search-engine/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/simple-search-engine/","name":"Create a simple search engine"}}]}
```

---

---
title: Talk to your knowledge base
description: Build a voice agent that lets users speak to an AI Search knowledge base and hear spoken answers, using the Cloudflare Agents voice pipeline.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Talk to your knowledge base

This tutorial builds a voice agent that you can talk to and that answers out loud from your [AI Search](https://developers.cloudflare.com/ai-search/) knowledge base. It uses the [Cloudflare Agents](https://developers.cloudflare.com/agents/) [@cloudflare/voice](https://developers.cloudflare.com/agents/communication-channels/voice/) package for the speech pipeline, and AI Search as the agent's knowledge base, exposed as a retrieval tool the agent's model calls.

**What you will build:** A voice agent that transcribes your speech, calls AI Search to retrieve relevant content from your indexed knowledge base, generates a grounded answer, and speaks it back.

## How it works

The `@cloudflare/voice` package adds a full voice pipeline to a Cloudflare Agent: speech-to-text (STT), a "turn" handler where you produce a reply, and text-to-speech (TTS). The pipeline runs in a single Worker backed by a Durable Object, and the browser connects to it over a WebSocket.

The one method you write is `onTurn()`, which receives the user's transcript and returns the text to speak. This is where AI Search fits in: you run a language model and give it AI Search as a retrieval tool. The model decides when to search your knowledge base, grounds its reply in the results it gets back, and returns the answer text, which the pipeline speaks.

Browser Mic 

[ Workers AI Speech-to-text ](https://developers.cloudflare.com/workers-ai/) 

Cloudflare Agents onTurn(transcript) 

tool [ AI Search retrieval ](https://developers.cloudflare.com/ai-search/) 

[ Workers AI Text-to-speech ](https://developers.cloudflare.com/workers-ai/) 

Browser Speaker 

Note

The voice pipeline uses [Workers AI](https://developers.cloudflare.com/workers-ai/) for STT and TTS, so no extra API keys are required. `@cloudflare/voice` is in beta.

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), discussed later in this guide, requires a Node version of `16.17.0` or later.

You also need an AI Search instance that already contains indexed content. This is the knowledge base you will speak to. To create one and add content, refer to [Get started](https://developers.cloudflare.com/ai-search/get-started/).

## 1\. Create the voice agent

Scaffold a Cloudflare Agents project with the voice starter template, which includes the Durable Object wiring and a React client:

```sh
npm create cloudflare@latest voice-knowledge-base -- --template cloudflare/agents-starter
cd voice-knowledge-base
```

Install the voice package:

 npm  yarn  pnpm  bun 

```
npm i @cloudflare/voice
```

```
yarn add @cloudflare/voice
```

```
pnpm add @cloudflare/voice
```

```
bun add @cloudflare/voice
```

The [@cloudflare/voice](https://developers.cloudflare.com/agents/communication-channels/voice/) package provides the `withVoice` mixin and the Workers AI providers (`WorkersAIFluxSTT` and `WorkersAITTS`). For a full walkthrough of the voice agent itself, including the browser client, refer to the [Voice agent example](https://developers.cloudflare.com/agents/examples/voice-agent/).

## 2\. Add the AI Search binding

Add an [AI Search binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) to your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/), alongside the Workers AI binding and the agent's Durable Object. Replace `my-instance` with the name of your instance.

* [  wrangler.jsonc ](#tab-panel-7249)
* [  wrangler.toml ](#tab-panel-7250)

**JSONC**

```jsonc
{
  "name": "voice-knowledge-base",
  "main": "src/server.ts",
  // Set this to today's date
  "compatibility_date": "2026-07-14",
  "compatibility_flags": ["nodejs_compat"],
  "ai": {
    "binding": "AI",
    "remote": true
  },
  "ai_search": [
    {
      "binding": "AI_SEARCH",
      "instance_name": "my-instance",
      "remote": true
    }
  ],
  "durable_objects": {
    "bindings": [
      {
        "name": "TalkToDocs",
        "class_name": "TalkToDocs"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["TalkToDocs"]
    }
  ]
}
```

**TOML**

```toml
name = "voice-knowledge-base"
main = "src/server.ts"
# Set this to today's date
compatibility_date = "2026-07-14"
compatibility_flags = [ "nodejs_compat" ]


[ai]
binding = "AI"
remote = true


[[ai_search]]
binding = "AI_SEARCH"
instance_name = "my-instance"
remote = true


[[durable_objects.bindings]]
name = "TalkToDocs"
class_name = "TalkToDocs"


[[migrations]]
tag = "v1"
new_sqlite_classes = [ "TalkToDocs" ]
```

Note

The AI Search binding requires a `compatibility_date` of `2026-03-27` or later.

Regenerate your binding types so `env.AI` and `env.AI_SEARCH` are typed:

 npm  yarn  pnpm 

```
npx wrangler types
```

```
yarn wrangler types
```

```
pnpm wrangler types
```

## 3\. Answer from your knowledge base

Update `src/server.ts`. Build the agent with the `withVoice` mixin, set the STT and TTS providers, and in `onTurn()` run a Workers AI model that calls AI Search as a retrieval tool.

The model is given a `searchKnowledgeBase` tool that calls AI Search's `search()` for retrieval. It calls the tool when it needs facts from your knowledge base, grounds its answer in the returned chunks, and you return the generated text for the pipeline to speak. The agent stores conversation history automatically, so you can pass `context.messages` for follow-up questions.

* [  JavaScript ](#tab-panel-7251)
* [  TypeScript ](#tab-panel-7252)

**src/server.js**

```js
import { Agent, routeAgentRequest } from "agents";
import { withVoice, WorkersAIFluxSTT, WorkersAITTS } from "@cloudflare/voice";
import { generateText, tool, stepCountIs } from "ai";
import { createWorkersAI } from "workers-ai-provider";
import { z } from "zod";


const VoiceAgent = withVoice(Agent);


export class TalkToDocs extends VoiceAgent {
  // Workers AI powers speech-to-text and text-to-speech (no API keys needed).
  transcriber = new WorkersAIFluxSTT(this.env.AI);
  tts = new WorkersAITTS(this.env.AI);


  // Called each time the user finishes speaking. The agent's model generates
  // the reply, calling AI Search as a retrieval tool when it needs facts from
  // your knowledge base.
  async onTurn(transcript, context) {
    const workersai = createWorkersAI({ binding: this.env.AI });


    const result = await generateText({
      // Use a Workers AI model that supports function calling.
      model: workersai("@cf/zai-org/glm-5.2"),
      system:
        "You are a helpful voice assistant that answers from a Cloudflare AI Search knowledge base. " +
        "For questions about the product, call the searchKnowledgeBase tool first and answer using the results. " +
        "Skip the tool for greetings and small talk. Keep replies short and conversational.",
      messages: [
        ...context.messages.map((message) => ({
          role: message.role,
          content: message.content,
        })),
        { role: "user", content: transcript },
      ],
      tools: {
        searchKnowledgeBase: tool({
          description:
            "Search the knowledge base for information to answer the user's question.",
          inputSchema: z.object({
            query: z.string().describe("A focused search query"),
          }),
          execute: async ({ query }) => {
            // search() runs retrieval only and returns the matching chunks.
            const res = await this.env.AI_SEARCH.search({
              query,
              ai_search_options: { retrieval: { max_num_results: 5 } },
            });
            return res.chunks.map((chunk) => chunk.text).join("\n\n");
          },
        }),
      },
      // Let the model call the tool, then answer from the results.
      stopWhen: stepCountIs(4),
    });


    // Return the generated answer for the pipeline to speak.
    return result.text;
  }
}


export default {
  async fetch(request, env) {
    return (
      (await routeAgentRequest(request, env)) ??
      new Response("Not found", { status: 404 })
    );
  },
};
```

**src/server.ts**

```ts
import { Agent, routeAgentRequest } from "agents";
import {
  withVoice,
  WorkersAIFluxSTT,
  WorkersAITTS,
  type VoiceTurnContext,
} from "@cloudflare/voice";
import { generateText, tool, stepCountIs } from "ai";
import { createWorkersAI } from "workers-ai-provider";
import { z } from "zod";


const VoiceAgent = withVoice(Agent);


export class TalkToDocs extends VoiceAgent<Env> {
  // Workers AI powers speech-to-text and text-to-speech (no API keys needed).
  transcriber = new WorkersAIFluxSTT(this.env.AI);
  tts = new WorkersAITTS(this.env.AI);


  // Called each time the user finishes speaking. The agent's model generates
  // the reply, calling AI Search as a retrieval tool when it needs facts from
  // your knowledge base.
  async onTurn(transcript: string, context: VoiceTurnContext) {
    const workersai = createWorkersAI({ binding: this.env.AI });


    const result = await generateText({
      // Use a Workers AI model that supports function calling.
      model: workersai("@cf/zai-org/glm-5.2"),
      system:
        "You are a helpful voice assistant that answers from a Cloudflare AI Search knowledge base. " +
        "For questions about the product, call the searchKnowledgeBase tool first and answer using the results. " +
        "Skip the tool for greetings and small talk. Keep replies short and conversational.",
      messages: [
        ...context.messages.map((message) => ({
          role: message.role as "user" | "assistant",
          content: message.content,
        })),
        { role: "user" as const, content: transcript },
      ],
      tools: {
        searchKnowledgeBase: tool({
          description:
            "Search the knowledge base for information to answer the user's question.",
          inputSchema: z.object({
            query: z.string().describe("A focused search query"),
          }),
          execute: async ({ query }) => {
            // search() runs retrieval only and returns the matching chunks.
            const res = await this.env.AI_SEARCH.search({
              query,
              ai_search_options: { retrieval: { max_num_results: 5 } },
            });
            return res.chunks.map((chunk) => chunk.text).join("\n\n");
          },
        }),
      },
      // Let the model call the tool, then answer from the results.
      stopWhen: stepCountIs(4),
    });


    // Return the generated answer for the pipeline to speak.
    return result.text;
  }
}


export default {
  async fetch(request: Request, env: Env) {
    return (
      (await routeAgentRequest(request, env)) ??
      new Response("Not found", { status: 404 })
    );
  },
} satisfies ExportedHandler<Env>;
```

Each chunk returned by `search()` includes its source item and a relevance score, so you can surface citations or log what the agent retrieved.

Note

Use a Workers AI model that supports function calling so the model can call the tool. This example returns the resolved answer text from `onTurn()`.

## 4\. Build the client

Replace `src/client.tsx` with a React component that uses the [useVoiceAgent](https://developers.cloudflare.com/agents/communication-channels/voice/) hook. The hook manages the microphone, the WebSocket connection to your agent, audio playback, and interrupt detection, so the component only needs to render controls. Set `agent` to your agent class name, `TalkToDocs`.

**src/client.tsx**

```tsx
import { useVoiceAgent } from "@cloudflare/voice/react";


function App() {
  // useVoiceAgent connects to your agent over WebSocket, captures the
  // microphone, plays the spoken response, and exposes the live call state.
  // `agent` matches your Durable Object class name.
  const {
    // Pipeline state: "idle" | "listening" | "thinking" | "speaking".
    status,
    // Finalized conversation turns (your speech and the agent's replies).
    transcript,
    // Live partial transcription of what you are currently saying.
    interimTranscript,
    // Whether the WebSocket connection to the agent is open.
    connected,
    startCall,
    endCall,
    toggleMute,
    isMuted,
  } = useVoiceAgent({ agent: "TalkToDocs" });


  const inCall = status !== "idle";


  return (
    <div>
      <h1>Talk to your knowledge base</h1>


      {/* Shows "thinking" while the agent searches the KB and generates a reply. */}
      <p>Status: {status}</p>


      {/* Toggle the call. Disabled until the agent connection is open. */}
      <button
        onClick={inCall ? endCall : startCall}
        disabled={!connected && !inCall}
      >
        {!connected ? "Connecting…" : inCall ? "End call" : "Start call"}
      </button>


      {/* Mute only applies once a call is active. */}
      {inCall && (
        <button onClick={toggleMute}>{isMuted ? "Unmute" : "Mute"}</button>
      )}


      {/* Lightweight loading state while the agent works on a reply. */}
      {status === "thinking" && <p>Thinking…</p>}


      {/* Live partial transcript, updated as you speak. */}
      {interimTranscript && (
        <p>
          <em>{interimTranscript}</em>
        </p>
      )}


      {/* Finalized turns from both you and the agent. */}
      {transcript.map((message, index) => (
        <p key={index}>
          <strong>{message.role}:</strong> {message.text}
        </p>
      ))}
    </div>
  );
}


export default App;
```

The hook handles the microphone and playback, so there is no push-to-talk button. The model detects when you finish speaking, runs `onTurn()`, and plays the spoken answer back automatically.

## 5\. Run it locally

Start a local development server:

 npm  yarn  pnpm 

```
npm run dev
```

```
yarn run dev
```

```
pnpm run dev
```

Open the app in your browser, select **Start call** and allow microphone access, then ask a question that your content can answer. You will see your words transcribed in real time, and the agent speaks its answer from your knowledge base. The `status` value moves through `listening`, `thinking`, and `speaking` as it works.

## 6\. Deploy

Deploy your agent to make it available on the Internet:

 npm  yarn  pnpm 

```
npx wrangler deploy
```

```
yarn wrangler deploy
```

```
pnpm wrangler deploy
```

## Talk to your knowledge base with multiple people

This tutorial builds a single-user voice agent. If you instead need several people in a live room talking to your knowledge base together, use [RealtimeKit](https://developers.cloudflare.com/realtime/realtimekit/) for the multi-party audio and video layer, and keep this voice agent as the component that answers from AI Search. RealtimeKit provides the meeting room and transcription, but it does not host the answer engine.

## Next steps

[ Voice agents API reference ](https://developers.cloudflare.com/agents/communication-channels/voice/) The @cloudflare/voice pipeline, providers, and onTurn contract. 

[ Voice agent example ](https://developers.cloudflare.com/agents/examples/voice-agent/) A full walkthrough of the voice agent and its browser client. 

[ Search Workers binding ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) Full reference for chatCompletions() and search() from a Worker. 

[ Bring your own generation model ](https://developers.cloudflare.com/ai-search/how-to/bring-your-own-generation-model/) Use a third-party model for generation while AI Search handles retrieval.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/how-to/talk-to-your-knowledge-base/#page","headline":"Talk to your knowledge base · Cloudflare AI Search docs","description":"Build a voice agent that lets users speak to an AI Search knowledge base and hear spoken answers, using the Cloudflare Agents voice pipeline.","url":"https://developers.cloudflare.com/ai-search/how-to/talk-to-your-knowledge-base/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-10","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/how-to/","name":"How to"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/how-to/talk-to-your-knowledge-base/","name":"Talk to your knowledge base"}}]}
```

---

# AI Search

# Namespaces

## List namespaces.

**get** `/accounts/{account_id}/ai-search/namespaces`

List namespaces.

### Path Parameters

- `account_id: string`

### Query Parameters

- `page: optional number`

  Page number (1-indexed).

- `per_page: optional number`

  Number of results per page.

- `search: optional string`

  Filter namespaces whose name or description contains this string (case-insensitive).

### Returns

- `result: array of object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "created_at": "2019-12-27T18:11:19.117Z",
      "name": "production",
      "description": "Production environment"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Create namespace.

**post** `/accounts/{account_id}/ai-search/namespaces`

Create a new namespace.

### Path Parameters

- `account_id: string`

### Body Parameters

- `name: string`

- `description: optional string`

  Optional description for the namespace. Max 256 characters.

### Returns

- `result: object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "name": "name",
          "description": "Production environment"
        }'
```

#### Response

```json
{
  "result": {
    "created_at": "2019-12-27T18:11:19.117Z",
    "name": "production",
    "description": "Production environment"
  },
  "success": true
}
```

## Read namespace.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}`

Read namespace.

### Path Parameters

- `account_id: string`

- `name: string`

### Returns

- `result: object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "created_at": "2019-12-27T18:11:19.117Z",
    "name": "production",
    "description": "Production environment"
  },
  "success": true
}
```

## Update namespace.

**put** `/accounts/{account_id}/ai-search/namespaces/{name}`

Update namespace.

### Path Parameters

- `account_id: string`

- `name: string`

### Body Parameters

- `description: optional string`

  Optional description for the namespace. Max 256 characters.

### Returns

- `result: object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME \
    -X PUT \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "created_at": "2019-12-27T18:11:19.117Z",
    "name": "production",
    "description": "Production environment"
  },
  "success": true
}
```

## Delete namespace.

**delete** `/accounts/{account_id}/ai-search/namespaces/{name}`

Permanently delete a namespace. The namespace must be empty (no instances).

### Path Parameters

- `account_id: string`

- `name: string`

### Returns

- `result: unknown`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME \
    -X DELETE \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {},
  "success": true
}
```

## Multi-Instance Search

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/search`

Performs a semantic search query against multiple AI Search instances in parallel, merging the retrieved results into a single ranked response.

### Path Parameters

- `account_id: string`

- `name: string`

### Body Parameters

- `ai_search_options: object { instance_ids, cache, query_rewrite, 2 more }`

  - `instance_ids: array of string`

  - `cache: optional object { cache_threshold, enabled }`

    - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

      - `"super_strict_match"`

      - `"close_enough"`

      - `"flexible_friend"`

      - `"anything_goes"`

    - `enabled: optional boolean`

  - `query_rewrite: optional object { enabled, model, rewrite_prompt }`

    - `enabled: optional boolean`

    - `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

      - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

      - `"@cf/zai-org/glm-4.7-flash"`

      - `"@cf/meta/llama-3.1-8b-instruct-fast"`

      - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

      - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

      - `"@cf/qwen/qwen3-30b-a3b-fp8"`

      - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

      - `"@cf/moonshotai/kimi-k2-instruct"`

      - `"@cf/google/gemma-3-12b-it"`

      - `"@cf/google/gemma-4-26b-a4b-it"`

      - `"@cf/moonshotai/kimi-k2.5"`

      - `"anthropic/claude-3-7-sonnet"`

      - `"anthropic/claude-sonnet-4"`

      - `"anthropic/claude-opus-4"`

      - `"anthropic/claude-3-5-haiku"`

      - `"cerebras/qwen-3-235b-a22b-instruct"`

      - `"cerebras/qwen-3-235b-a22b-thinking"`

      - `"cerebras/llama-3.3-70b"`

      - `"cerebras/llama-4-maverick-17b-128e-instruct"`

      - `"cerebras/llama-4-scout-17b-16e-instruct"`

      - `"cerebras/gpt-oss-120b"`

      - `"google-ai-studio/gemini-2.5-flash"`

      - `"google-ai-studio/gemini-2.5-pro"`

      - `"grok/grok-4"`

      - `"groq/llama-3.3-70b-versatile"`

      - `"groq/llama-3.1-8b-instant"`

      - `"openai/gpt-5"`

      - `"openai/gpt-5-mini"`

      - `"openai/gpt-5-nano"`

      - `""`

    - `rewrite_prompt: optional string`

  - `reranking: optional object { enabled, match_threshold, model }`

    - `enabled: optional boolean`

    - `match_threshold: optional number`

    - `model: optional "@cf/baai/bge-reranker-base" or ""`

      - `"@cf/baai/bge-reranker-base"`

      - `""`

  - `retrieval: optional object { boost_by, context_expansion, filters, 6 more }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Overrides the instance-level boost_by config. Direction defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `context_expansion: optional number`

    - `filters: optional map[unknown]`

    - `fusion_method: optional "max" or "rrf"`

      - `"max"`

      - `"rrf"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted, falls back to the instance-level retrieval_options.keyword_match_mode, then to 'and'.

      - `"and"`

      - `"or"`

    - `match_threshold: optional number`

    - `max_num_results: optional number`

    - `retrieval_type: optional "vector" or "keyword" or "hybrid"`

      - `"vector"`

      - `"keyword"`

      - `"hybrid"`

    - `return_on_failure: optional boolean`

- `messages: optional array of object { content, role }`

  OpenAI-compatible message array. For multimodal queries, set the last user message's `content` to an array of typed parts: `[{type:'text', text:'…'}, {type:'image_url', image_url:{url:'…'}}]`. Image inputs require the RAG's embedding_model to declare 'image' in supported_modalities.

  - `content: string or array of object { text, type }  or object { image_url, type }`

    - `string`

    - `array of object { text, type }  or object { image_url, type }`

      - `object { text, type }`

        - `text: string`

        - `type: "text"`

          - `"text"`

      - `object { image_url, type }`

        - `image_url: object { url }`

          - `url: string`

        - `type: "image_url"`

          - `"image_url"`

  - `role: "system" or "developer" or "user" or 2 more`

    - `"system"`

    - `"developer"`

    - `"user"`

    - `"assistant"`

    - `"tool"`

- `query: optional string`

  A simple text query string. Alternative to 'messages' — provide either this or 'messages', not both.

### Returns

- `result: object { chunks, query_kind, errors, search_query }`

  - `chunks: array of object { id, instance_id, score, 4 more }`

    - `id: string`

    - `instance_id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `query_kind: "text" or "image" or "multimodal"`

    - `"text"`

    - `"image"`

    - `"multimodal"`

  - `errors: optional array of object { instance_id, message }`

    - `instance_id: string`

    - `message: string`

  - `search_query: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/search \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "ai_search_options": {
            "instance_ids": [
              "my-ai-search"
            ]
          }
        }'
```

#### Response

```json
{
  "result": {
    "chunks": [
      {
        "id": "id",
        "instance_id": "instance_id",
        "score": 0,
        "text": "text",
        "type": "type",
        "item": {
          "key": "key",
          "metadata": {
            "foo": "bar"
          },
          "timestamp": 0
        },
        "scoring_details": {
          "fusion_method": "rrf",
          "keyword_rank": 0,
          "keyword_score": 0,
          "reranking_score": 0,
          "vector_rank": 0,
          "vector_score": 0
        }
      }
    ],
    "query_kind": "text",
    "errors": [
      {
        "instance_id": "instance_id",
        "message": "message"
      }
    ],
    "search_query": "search_query"
  },
  "success": true
}
```

## Multi-Instance Chat Completions

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/chat/completions`

Performs a chat completion request against multiple AI Search instances in parallel, merging retrieved content as context for generating a response.

### Path Parameters

- `account_id: string`

- `name: string`

### Body Parameters

- `ai_search_options: object { instance_ids, cache, query_rewrite, 2 more }`

  - `instance_ids: array of string`

  - `cache: optional object { cache_threshold, enabled }`

    - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

      - `"super_strict_match"`

      - `"close_enough"`

      - `"flexible_friend"`

      - `"anything_goes"`

    - `enabled: optional boolean`

  - `query_rewrite: optional object { enabled, model, rewrite_prompt }`

    - `enabled: optional boolean`

    - `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

      - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

      - `"@cf/zai-org/glm-4.7-flash"`

      - `"@cf/meta/llama-3.1-8b-instruct-fast"`

      - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

      - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

      - `"@cf/qwen/qwen3-30b-a3b-fp8"`

      - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

      - `"@cf/moonshotai/kimi-k2-instruct"`

      - `"@cf/google/gemma-3-12b-it"`

      - `"@cf/google/gemma-4-26b-a4b-it"`

      - `"@cf/moonshotai/kimi-k2.5"`

      - `"anthropic/claude-3-7-sonnet"`

      - `"anthropic/claude-sonnet-4"`

      - `"anthropic/claude-opus-4"`

      - `"anthropic/claude-3-5-haiku"`

      - `"cerebras/qwen-3-235b-a22b-instruct"`

      - `"cerebras/qwen-3-235b-a22b-thinking"`

      - `"cerebras/llama-3.3-70b"`

      - `"cerebras/llama-4-maverick-17b-128e-instruct"`

      - `"cerebras/llama-4-scout-17b-16e-instruct"`

      - `"cerebras/gpt-oss-120b"`

      - `"google-ai-studio/gemini-2.5-flash"`

      - `"google-ai-studio/gemini-2.5-pro"`

      - `"grok/grok-4"`

      - `"groq/llama-3.3-70b-versatile"`

      - `"groq/llama-3.1-8b-instant"`

      - `"openai/gpt-5"`

      - `"openai/gpt-5-mini"`

      - `"openai/gpt-5-nano"`

      - `""`

    - `rewrite_prompt: optional string`

  - `reranking: optional object { enabled, match_threshold, model }`

    - `enabled: optional boolean`

    - `match_threshold: optional number`

    - `model: optional "@cf/baai/bge-reranker-base" or ""`

      - `"@cf/baai/bge-reranker-base"`

      - `""`

  - `retrieval: optional object { boost_by, context_expansion, filters, 6 more }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Overrides the instance-level boost_by config. Direction defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `context_expansion: optional number`

    - `filters: optional map[unknown]`

    - `fusion_method: optional "max" or "rrf"`

      - `"max"`

      - `"rrf"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted, falls back to the instance-level retrieval_options.keyword_match_mode, then to 'and'.

      - `"and"`

      - `"or"`

    - `match_threshold: optional number`

    - `max_num_results: optional number`

    - `retrieval_type: optional "vector" or "keyword" or "hybrid"`

      - `"vector"`

      - `"keyword"`

      - `"hybrid"`

    - `return_on_failure: optional boolean`

- `messages: array of object { content, role }`

  - `content: string or array of object { text, type }  or object { image_url, type }`

    - `string`

    - `array of object { text, type }  or object { image_url, type }`

      - `object { text, type }`

        - `text: string`

        - `type: "text"`

          - `"text"`

      - `object { image_url, type }`

        - `image_url: object { url }`

          - `url: string`

        - `type: "image_url"`

          - `"image_url"`

  - `role: "system" or "developer" or "user" or 2 more`

    - `"system"`

    - `"developer"`

    - `"user"`

    - `"assistant"`

    - `"tool"`

- `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `stream: optional boolean`

### Returns

- `choices: array of object { message, index }`

  - `message: object { content, role }`

    - `content: string or array of object { text, type }  or object { image_url, type }`

      - `string`

      - `array of object { text, type }  or object { image_url, type }`

        - `object { text, type }`

          - `text: string`

          - `type: "text"`

            - `"text"`

        - `object { image_url, type }`

          - `image_url: object { url }`

            - `url: string`

          - `type: "image_url"`

            - `"image_url"`

    - `role: "system" or "developer" or "user" or 2 more`

      - `"system"`

      - `"developer"`

      - `"user"`

      - `"assistant"`

      - `"tool"`

  - `index: optional number`

- `chunks: array of object { id, instance_id, score, 4 more }`

  - `id: string`

  - `instance_id: string`

  - `score: number`

  - `text: string`

  - `type: string`

  - `item: optional object { key, metadata, timestamp }`

    - `key: string`

    - `metadata: optional map[unknown]`

    - `timestamp: optional number`

  - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

    - `fusion_method: optional "rrf" or "max"`

      - `"rrf"`

      - `"max"`

    - `keyword_rank: optional number`

    - `keyword_score: optional number`

    - `reranking_score: optional number`

    - `vector_rank: optional number`

    - `vector_score: optional number`

- `id: optional string`

- `errors: optional array of object { instance_id, message }`

  - `instance_id: string`

  - `message: string`

- `model: optional string`

- `object: optional string`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/chat/completions \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "ai_search_options": {
            "instance_ids": [
              "my-ai-search"
            ]
          },
          "messages": [
            {
              "content": "string",
              "role": "system"
            }
          ]
        }'
```

#### Response

```json
{
  "choices": [
    {
      "message": {
        "content": "string",
        "role": "system"
      },
      "index": 0
    }
  ],
  "chunks": [
    {
      "id": "id",
      "instance_id": "instance_id",
      "score": 0,
      "text": "text",
      "type": "type",
      "item": {
        "key": "key",
        "metadata": {
          "foo": "bar"
        },
        "timestamp": 0
      },
      "scoring_details": {
        "fusion_method": "rrf",
        "keyword_rank": 0,
        "keyword_score": 0,
        "reranking_score": 0,
        "vector_rank": 0,
        "vector_score": 0
      }
    }
  ],
  "id": "id",
  "errors": [
    {
      "instance_id": "instance_id",
      "message": "message"
    }
  ],
  "model": "model",
  "object": "object"
}
```

## Domain Types

### Namespace List Response

- `NamespaceListResponse object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

### Namespace Create Response

- `NamespaceCreateResponse object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

### Namespace Read Response

- `NamespaceReadResponse object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

### Namespace Update Response

- `NamespaceUpdateResponse object { created_at, name, description }`

  - `created_at: string`

  - `name: string`

  - `description: optional string`

    Optional description for the namespace. Max 256 characters.

### Namespace Delete Response

- `NamespaceDeleteResponse = unknown`

### Namespace Search Response

- `NamespaceSearchResponse object { chunks, query_kind, errors, search_query }`

  - `chunks: array of object { id, instance_id, score, 4 more }`

    - `id: string`

    - `instance_id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `query_kind: "text" or "image" or "multimodal"`

    - `"text"`

    - `"image"`

    - `"multimodal"`

  - `errors: optional array of object { instance_id, message }`

    - `instance_id: string`

    - `message: string`

  - `search_query: optional string`

### Namespace Chat Completions Response

- `NamespaceChatCompletionsResponse object { choices, chunks, id, 3 more }`

  - `choices: array of object { message, index }`

    - `message: object { content, role }`

      - `content: string or array of object { text, type }  or object { image_url, type }`

        - `string`

        - `array of object { text, type }  or object { image_url, type }`

          - `object { text, type }`

            - `text: string`

            - `type: "text"`

              - `"text"`

          - `object { image_url, type }`

            - `image_url: object { url }`

              - `url: string`

            - `type: "image_url"`

              - `"image_url"`

      - `role: "system" or "developer" or "user" or 2 more`

        - `"system"`

        - `"developer"`

        - `"user"`

        - `"assistant"`

        - `"tool"`

    - `index: optional number`

  - `chunks: array of object { id, instance_id, score, 4 more }`

    - `id: string`

    - `instance_id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `id: optional string`

  - `errors: optional array of object { instance_id, message }`

    - `instance_id: string`

    - `message: string`

  - `model: optional string`

  - `object: optional string`

# Instances

## List AI Search instances.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances`

List all AI Search instances in the account.

### Path Parameters

- `account_id: string`

- `name: string`

### Query Parameters

- `namespace: optional string`

  Filter by namespace.

- `order_by: optional "created_at"`

  Field to order results by.

  - `"created_at"`

- `order_by_direction: optional "asc" or "desc"`

  Order direction.

  - `"asc"`

  - `"desc"`

- `page: optional number`

  Page number (1-indexed).

- `per_page: optional number`

  Number of results per page.

- `search: optional string`

  Filter instances whose id contains this string (case-insensitive).

### Returns

- `result: array of object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "my-ai-search",
      "created_at": "2019-12-27T18:11:19.117Z",
      "modified_at": "2019-12-27T18:11:19.117Z",
      "ai_gateway_id": "ai_gateway_id",
      "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
      "cache": true,
      "cache_threshold": "super_strict_match",
      "cache_ttl": 600,
      "chunk_overlap": 0,
      "chunk_size": 64,
      "created_by": "created_by",
      "custom_metadata": [
        {
          "data_type": "text",
          "field_name": "x"
        }
      ],
      "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
      "enable": true,
      "engine_version": 0,
      "fusion_method": "max",
      "hybrid_search_enabled": true,
      "index_method": {
        "keyword": true,
        "vector": true
      },
      "indexing_options": {
        "keyword_tokenizer": "porter"
      },
      "last_activity": "2019-12-27T18:11:19.117Z",
      "max_num_results": 1,
      "metadata": {
        "created_from_aisearch_wizard": true,
        "worker_domain": "worker_domain"
      },
      "modified_by": "modified_by",
      "namespace": "namespace",
      "paused": true,
      "public_endpoint_id": "public_endpoint_id",
      "public_endpoint_params": {
        "authorized_hosts": [
          "string"
        ],
        "chat_completions_endpoint": {
          "disabled": true
        },
        "custom_domains": [
          "search.example.com"
        ],
        "enabled": true,
        "mcp": {
          "description": "description",
          "disabled": true
        },
        "rate_limit": {
          "period_ms": 60000,
          "requests": 1,
          "technique": "fixed"
        },
        "search_endpoint": {
          "disabled": true
        }
      },
      "reranking": true,
      "reranking_model": "@cf/baai/bge-reranker-base",
      "retrieval_options": {
        "boost_by": [
          {
            "field": "timestamp",
            "direction": "desc"
          }
        ],
        "keyword_match_mode": "and"
      },
      "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
      "rewrite_query": true,
      "score_threshold": 0,
      "source": "source",
      "source_params": {
        "exclude_items": [
          "/admin/**",
          "/private/**",
          "**\\temp\\**"
        ],
        "include_items": [
          "/blog/**",
          "/docs/**/*.html",
          "**\\blog\\**.html"
        ],
        "prefix": "prefix",
        "r2_jurisdiction": "r2_jurisdiction",
        "web_crawler": {
          "parse_options": {
            "content_selector": [
              {
                "path": "**/blog/**",
                "selector": "article div.post-body"
              },
              {
                "path": "**/docs/**",
                "selector": "main"
              }
            ],
            "include_headers": {
              "cache-control": "no-cache, no-store"
            },
            "include_images": true,
            "specific_sitemaps": [
              "https://example.com/sitemap.xml",
              "https://example.com/blog-sitemap.xml"
            ],
            "use_browser_rendering": true
          },
          "parse_type": "sitemap"
        }
      },
      "status": "status",
      "sync_interval": 900,
      "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
      "type": "r2"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Create an AI Search instance.

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/instances`

Create a new AI Search instance with the given configuration.

### Path Parameters

- `account_id: string`

- `name: string`

### Body Parameters

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `ai_gateway_id: optional string`

- `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `cache: optional boolean`

- `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

  - `"super_strict_match"`

  - `"close_enough"`

  - `"flexible_friend"`

  - `"anything_goes"`

- `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

  Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

  - `600`

  - `1800`

  - `3600`

  - `7200`

  - `21600`

  - `43200`

  - `86400`

  - `172800`

  - `259200`

  - `518400`

- `chunk: optional boolean`

- `chunk_overlap: optional number`

- `chunk_size: optional number`

- `custom_metadata: optional array of object { data_type, field_name }`

  - `data_type: "text" or "number" or "boolean" or "datetime"`

    - `"text"`

    - `"number"`

    - `"boolean"`

    - `"datetime"`

  - `field_name: string`

- `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

  - `"@cf/qwen/qwen3-embedding-0.6b"`

  - `"@cf/qwen/qwen3-vl-embedding-2b"`

  - `"@cf/baai/bge-m3"`

  - `"@cf/baai/bge-large-en-v1.5"`

  - `"@cf/google/embeddinggemma-300m"`

  - `"google-ai-studio/gemini-embedding-001"`

  - `"google-ai-studio/gemini-embedding-2-preview"`

  - `"google-ai-studio/gemini-embedding-2"`

  - `"openai/text-embedding-3-small"`

  - `"openai/text-embedding-3-large"`

  - `""`

- `fusion_method: optional "max" or "rrf"`

  - `"max"`

  - `"rrf"`

- `hybrid_search_enabled: optional boolean`

  Deprecated — use index_method instead.

- `index_method: optional object { keyword, vector }`

  Controls which storage backends are used during indexing. Defaults to vector-only.

  - `keyword: boolean`

    Enable keyword (BM25) storage backend.

  - `vector: boolean`

    Enable vector (embedding) storage backend.

- `indexing_options: optional object { keyword_tokenizer }`

  - `keyword_tokenizer: optional "porter" or "trigram"`

    Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

    - `"porter"`

    - `"trigram"`

- `max_num_results: optional number`

- `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

  - `created_from_aisearch_wizard: optional boolean`

  - `worker_domain: optional string`

- `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

  - `authorized_hosts: optional array of string`

  - `chat_completions_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable chat completions endpoint for this public endpoint

  - `custom_domains: optional array of string`

    Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

  - `enabled: optional boolean`

  - `mcp: optional object { description, disabled }`

    - `description: optional string`

    - `disabled: optional boolean`

      Disable MCP endpoint for this public endpoint

  - `rate_limit: optional object { period_ms, requests, technique }`

    - `period_ms: optional number`

    - `requests: optional number`

    - `technique: optional "fixed" or "sliding"`

      - `"fixed"`

      - `"sliding"`

  - `search_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable search endpoint for this public endpoint

- `reranking: optional boolean`

- `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

  - `"@cf/baai/bge-reranker-base"`

  - `""`

- `retrieval_options: optional object { boost_by, keyword_match_mode }`

  - `boost_by: optional array of object { field, direction }`

    Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

    - `field: string`

      Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

    - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

      Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

      - `"asc"`

      - `"desc"`

      - `"exists"`

      - `"not_exists"`

  - `keyword_match_mode: optional "and" or "or"`

    Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

    - `"and"`

    - `"or"`

- `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `rewrite_query: optional boolean`

- `score_threshold: optional number`

- `source: optional string`

- `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

  - `exclude_items: optional array of string`

    List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

  - `include_items: optional array of string`

    List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

  - `prefix: optional string`

  - `r2_jurisdiction: optional string`

  - `web_crawler: optional object { parse_options, parse_type }`

    - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

      - `content_selector: optional array of object { path, selector }`

        List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

        - `path: string`

          Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

        - `selector: string`

          CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

      - `include_headers: optional map[string]`

        Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

      - `include_images: optional boolean`

      - `specific_sitemaps: optional array of string`

        List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

      - `use_browser_rendering: optional boolean`

    - `parse_type: optional "sitemap" or "crawl"`

      - `"sitemap"`

      - `"crawl"`

- `sync_interval: optional 900 or 1800 or 3600 or 5 more`

  Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

  - `900`

  - `1800`

  - `3600`

  - `7200`

  - `14400`

  - `21600`

  - `43200`

  - `86400`

- `token_id: optional string`

- `type: optional "r2" or "web-crawler"`

  - `"r2"`

  - `"web-crawler"`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "id": "my-ai-search"
        }'
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Get an AI Search instance.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}`

Retrieve the configuration and status of an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Update an AI Search instance.

**put** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}`

Update the configuration of an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

### Body Parameters

- `ai_gateway_id: optional string`

- `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `cache: optional boolean`

- `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

  - `"super_strict_match"`

  - `"close_enough"`

  - `"flexible_friend"`

  - `"anything_goes"`

- `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

  Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

  - `600`

  - `1800`

  - `3600`

  - `7200`

  - `21600`

  - `43200`

  - `86400`

  - `172800`

  - `259200`

  - `518400`

- `chunk: optional boolean`

- `chunk_overlap: optional number`

- `chunk_size: optional number`

- `custom_metadata: optional array of object { data_type, field_name }`

  - `data_type: "text" or "number" or "boolean" or "datetime"`

    - `"text"`

    - `"number"`

    - `"boolean"`

    - `"datetime"`

  - `field_name: string`

- `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

  - `"@cf/qwen/qwen3-embedding-0.6b"`

  - `"@cf/qwen/qwen3-vl-embedding-2b"`

  - `"@cf/baai/bge-m3"`

  - `"@cf/baai/bge-large-en-v1.5"`

  - `"@cf/google/embeddinggemma-300m"`

  - `"google-ai-studio/gemini-embedding-001"`

  - `"google-ai-studio/gemini-embedding-2-preview"`

  - `"google-ai-studio/gemini-embedding-2"`

  - `"openai/text-embedding-3-small"`

  - `"openai/text-embedding-3-large"`

  - `""`

- `fusion_method: optional "max" or "rrf"`

  - `"max"`

  - `"rrf"`

- `index_method: optional object { keyword, vector }`

  Controls which storage backends are used during indexing. Defaults to vector-only.

  - `keyword: boolean`

    Enable keyword (BM25) storage backend.

  - `vector: boolean`

    Enable vector (embedding) storage backend.

- `indexing_options: optional object { keyword_tokenizer }`

  - `keyword_tokenizer: optional "porter" or "trigram"`

    Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

    - `"porter"`

    - `"trigram"`

- `max_num_results: optional number`

- `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

  - `created_from_aisearch_wizard: optional boolean`

  - `worker_domain: optional string`

- `paused: optional boolean`

- `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

  - `authorized_hosts: optional array of string`

  - `chat_completions_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable chat completions endpoint for this public endpoint

  - `custom_domains: optional array of string`

    Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

  - `enabled: optional boolean`

  - `mcp: optional object { description, disabled }`

    - `description: optional string`

    - `disabled: optional boolean`

      Disable MCP endpoint for this public endpoint

  - `rate_limit: optional object { period_ms, requests, technique }`

    - `period_ms: optional number`

    - `requests: optional number`

    - `technique: optional "fixed" or "sliding"`

      - `"fixed"`

      - `"sliding"`

  - `search_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable search endpoint for this public endpoint

- `reranking: optional boolean`

- `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

  - `"@cf/baai/bge-reranker-base"`

  - `""`

- `retrieval_options: optional object { boost_by, keyword_match_mode }`

  - `boost_by: optional array of object { field, direction }`

    Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

    - `field: string`

      Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

    - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

      Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

      - `"asc"`

      - `"desc"`

      - `"exists"`

      - `"not_exists"`

  - `keyword_match_mode: optional "and" or "or"`

    Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

    - `"and"`

    - `"or"`

- `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `rewrite_query: optional boolean`

- `score_threshold: optional number`

- `source: optional string`

- `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

  - `exclude_items: optional array of string`

    List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

  - `include_items: optional array of string`

    List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

  - `prefix: optional string`

  - `r2_jurisdiction: optional string`

  - `web_crawler: optional object { parse_options, parse_type }`

    - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

      - `content_selector: optional array of object { path, selector }`

        List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

        - `path: string`

          Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

        - `selector: string`

          CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

      - `include_headers: optional map[string]`

        Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

      - `include_images: optional boolean`

      - `specific_sitemaps: optional array of string`

        List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

      - `use_browser_rendering: optional boolean`

    - `parse_type: optional "sitemap" or "crawl"`

      - `"sitemap"`

      - `"crawl"`

- `summarization: optional boolean`

- `summarization_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `sync_interval: optional 900 or 1800 or 3600 or 5 more`

  Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

  - `900`

  - `1800`

  - `3600`

  - `7200`

  - `14400`

  - `21600`

  - `43200`

  - `86400`

- `system_prompt_ai_search: optional string`

- `system_prompt_index_summarization: optional string`

- `system_prompt_rewrite_query: optional string`

- `token_id: optional string`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID \
    -X PUT \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Delete an AI Search instance.

**delete** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}`

Permanently delete an AI Search instance and all its indexed data.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID \
    -X DELETE \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Get instance statistics.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/stats`

Retrieve usage and indexing statistics for an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Returns

- `result: object { completed, degraded, engine, 8 more }`

  - `completed: optional number`

  - `degraded: optional boolean`

    True when status counts are unavailable (e.g. legacy stats query exceeded D1 statement-size limit). Counts are omitted in this case.

  - `engine: optional object { r2, vectorize }`

    Engine-specific metadata. Present only for managed (v3) instances.

    - `r2: optional object { metadataSizeBytes, objectCount, payloadSizeBytes }`

      R2 bucket storage usage in bytes.

      - `metadataSizeBytes: number`

      - `objectCount: number`

      - `payloadSizeBytes: number`

    - `vectorize: optional object { dimensions, vectorsCount }`

      Vectorize index metadata (dimensions, vector count).

      - `dimensions: number`

      - `vectorsCount: number`

  - `error: optional number`

  - `file_embed_errors: optional map[unknown]`

  - `index_source_errors: optional map[unknown]`

  - `last_activity: optional string`

  - `outdated: optional number`

  - `queued: optional number`

  - `running: optional number`

  - `skipped: optional number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/stats \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "completed": 0,
    "degraded": true,
    "engine": {
      "r2": {
        "metadataSizeBytes": 0,
        "objectCount": 0,
        "payloadSizeBytes": 0
      },
      "vectorize": {
        "dimensions": 0,
        "vectorsCount": 0
      }
    },
    "error": 0,
    "file_embed_errors": {
      "foo": "bar"
    },
    "index_source_errors": {
      "foo": "bar"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "outdated": 0,
    "queued": 0,
    "running": 0,
    "skipped": 0
  },
  "success": true
}
```

## Search

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/search`

Executes a semantic search query against an AI Search instance to find relevant indexed content.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `ai_search_options: optional object { cache, query_rewrite, reranking, retrieval }`

  - `cache: optional object { cache_threshold, enabled }`

    - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

      - `"super_strict_match"`

      - `"close_enough"`

      - `"flexible_friend"`

      - `"anything_goes"`

    - `enabled: optional boolean`

  - `query_rewrite: optional object { enabled, model, rewrite_prompt }`

    - `enabled: optional boolean`

    - `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

      - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

      - `"@cf/zai-org/glm-4.7-flash"`

      - `"@cf/meta/llama-3.1-8b-instruct-fast"`

      - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

      - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

      - `"@cf/qwen/qwen3-30b-a3b-fp8"`

      - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

      - `"@cf/moonshotai/kimi-k2-instruct"`

      - `"@cf/google/gemma-3-12b-it"`

      - `"@cf/google/gemma-4-26b-a4b-it"`

      - `"@cf/moonshotai/kimi-k2.5"`

      - `"anthropic/claude-3-7-sonnet"`

      - `"anthropic/claude-sonnet-4"`

      - `"anthropic/claude-opus-4"`

      - `"anthropic/claude-3-5-haiku"`

      - `"cerebras/qwen-3-235b-a22b-instruct"`

      - `"cerebras/qwen-3-235b-a22b-thinking"`

      - `"cerebras/llama-3.3-70b"`

      - `"cerebras/llama-4-maverick-17b-128e-instruct"`

      - `"cerebras/llama-4-scout-17b-16e-instruct"`

      - `"cerebras/gpt-oss-120b"`

      - `"google-ai-studio/gemini-2.5-flash"`

      - `"google-ai-studio/gemini-2.5-pro"`

      - `"grok/grok-4"`

      - `"groq/llama-3.3-70b-versatile"`

      - `"groq/llama-3.1-8b-instant"`

      - `"openai/gpt-5"`

      - `"openai/gpt-5-mini"`

      - `"openai/gpt-5-nano"`

      - `""`

    - `rewrite_prompt: optional string`

  - `reranking: optional object { enabled, match_threshold, model }`

    - `enabled: optional boolean`

    - `match_threshold: optional number`

    - `model: optional "@cf/baai/bge-reranker-base" or ""`

      - `"@cf/baai/bge-reranker-base"`

      - `""`

  - `retrieval: optional object { boost_by, context_expansion, filters, 6 more }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Overrides the instance-level boost_by config. Direction defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `context_expansion: optional number`

    - `filters: optional map[unknown]`

    - `fusion_method: optional "max" or "rrf"`

      - `"max"`

      - `"rrf"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted, falls back to the instance-level retrieval_options.keyword_match_mode, then to 'and'.

      - `"and"`

      - `"or"`

    - `match_threshold: optional number`

    - `max_num_results: optional number`

    - `retrieval_type: optional "vector" or "keyword" or "hybrid"`

      - `"vector"`

      - `"keyword"`

      - `"hybrid"`

    - `return_on_failure: optional boolean`

- `messages: optional array of object { content, role }`

  OpenAI-compatible message array. For multimodal queries, set the last user message's `content` to an array of typed parts: `[{type:'text', text:'…'}, {type:'image_url', image_url:{url:'…'}}]`. Image inputs require the RAG's embedding_model to declare 'image' in supported_modalities.

  - `content: string or array of object { text, type }  or object { image_url, type }`

    - `string`

    - `array of object { text, type }  or object { image_url, type }`

      - `object { text, type }`

        - `text: string`

        - `type: "text"`

          - `"text"`

      - `object { image_url, type }`

        - `image_url: object { url }`

          - `url: string`

        - `type: "image_url"`

          - `"image_url"`

  - `role: "system" or "developer" or "user" or 2 more`

    - `"system"`

    - `"developer"`

    - `"user"`

    - `"assistant"`

    - `"tool"`

- `query: optional string`

  A simple text query string. Alternative to 'messages' — provide either this or 'messages', not both.

### Returns

- `result: object { chunks, query_kind, search_query }`

  - `chunks: array of object { id, score, text, 3 more }`

    - `id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `query_kind: "text" or "image" or "multimodal"`

    - `"text"`

    - `"image"`

    - `"multimodal"`

  - `search_query: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/search \
    -X POST \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "chunks": [
      {
        "id": "id",
        "score": 0,
        "text": "text",
        "type": "type",
        "item": {
          "key": "key",
          "metadata": {
            "foo": "bar"
          },
          "timestamp": 0
        },
        "scoring_details": {
          "fusion_method": "rrf",
          "keyword_rank": 0,
          "keyword_score": 0,
          "reranking_score": 0,
          "vector_rank": 0,
          "vector_score": 0
        }
      }
    ],
    "query_kind": "text",
    "search_query": "search_query"
  },
  "success": true
}
```

## Chat Completions

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/chat/completions`

Performs a chat completion request against an AI Search instance, using indexed content as context for generating responses.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `messages: array of object { content, role }`

  - `content: string or array of object { text, type }  or object { image_url, type }`

    - `string`

    - `array of object { text, type }  or object { image_url, type }`

      - `object { text, type }`

        - `text: string`

        - `type: "text"`

          - `"text"`

      - `object { image_url, type }`

        - `image_url: object { url }`

          - `url: string`

        - `type: "image_url"`

          - `"image_url"`

  - `role: "system" or "developer" or "user" or 2 more`

    - `"system"`

    - `"developer"`

    - `"user"`

    - `"assistant"`

    - `"tool"`

- `ai_search_options: optional object { cache, query_rewrite, reranking, retrieval }`

  - `cache: optional object { cache_threshold, enabled }`

    - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

      - `"super_strict_match"`

      - `"close_enough"`

      - `"flexible_friend"`

      - `"anything_goes"`

    - `enabled: optional boolean`

  - `query_rewrite: optional object { enabled, model, rewrite_prompt }`

    - `enabled: optional boolean`

    - `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

      - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

      - `"@cf/zai-org/glm-4.7-flash"`

      - `"@cf/meta/llama-3.1-8b-instruct-fast"`

      - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

      - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

      - `"@cf/qwen/qwen3-30b-a3b-fp8"`

      - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

      - `"@cf/moonshotai/kimi-k2-instruct"`

      - `"@cf/google/gemma-3-12b-it"`

      - `"@cf/google/gemma-4-26b-a4b-it"`

      - `"@cf/moonshotai/kimi-k2.5"`

      - `"anthropic/claude-3-7-sonnet"`

      - `"anthropic/claude-sonnet-4"`

      - `"anthropic/claude-opus-4"`

      - `"anthropic/claude-3-5-haiku"`

      - `"cerebras/qwen-3-235b-a22b-instruct"`

      - `"cerebras/qwen-3-235b-a22b-thinking"`

      - `"cerebras/llama-3.3-70b"`

      - `"cerebras/llama-4-maverick-17b-128e-instruct"`

      - `"cerebras/llama-4-scout-17b-16e-instruct"`

      - `"cerebras/gpt-oss-120b"`

      - `"google-ai-studio/gemini-2.5-flash"`

      - `"google-ai-studio/gemini-2.5-pro"`

      - `"grok/grok-4"`

      - `"groq/llama-3.3-70b-versatile"`

      - `"groq/llama-3.1-8b-instant"`

      - `"openai/gpt-5"`

      - `"openai/gpt-5-mini"`

      - `"openai/gpt-5-nano"`

      - `""`

    - `rewrite_prompt: optional string`

  - `reranking: optional object { enabled, match_threshold, model }`

    - `enabled: optional boolean`

    - `match_threshold: optional number`

    - `model: optional "@cf/baai/bge-reranker-base" or ""`

      - `"@cf/baai/bge-reranker-base"`

      - `""`

  - `retrieval: optional object { boost_by, context_expansion, filters, 6 more }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Overrides the instance-level boost_by config. Direction defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `context_expansion: optional number`

    - `filters: optional map[unknown]`

    - `fusion_method: optional "max" or "rrf"`

      - `"max"`

      - `"rrf"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted, falls back to the instance-level retrieval_options.keyword_match_mode, then to 'and'.

      - `"and"`

      - `"or"`

    - `match_threshold: optional number`

    - `max_num_results: optional number`

    - `retrieval_type: optional "vector" or "keyword" or "hybrid"`

      - `"vector"`

      - `"keyword"`

      - `"hybrid"`

    - `return_on_failure: optional boolean`

- `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `stream: optional boolean`

### Returns

- `choices: array of object { message, index }`

  - `message: object { content, role }`

    - `content: string or array of object { text, type }  or object { image_url, type }`

      - `string`

      - `array of object { text, type }  or object { image_url, type }`

        - `object { text, type }`

          - `text: string`

          - `type: "text"`

            - `"text"`

        - `object { image_url, type }`

          - `image_url: object { url }`

            - `url: string`

          - `type: "image_url"`

            - `"image_url"`

    - `role: "system" or "developer" or "user" or 2 more`

      - `"system"`

      - `"developer"`

      - `"user"`

      - `"assistant"`

      - `"tool"`

  - `index: optional number`

- `chunks: array of object { id, score, text, 3 more }`

  - `id: string`

  - `score: number`

  - `text: string`

  - `type: string`

  - `item: optional object { key, metadata, timestamp }`

    - `key: string`

    - `metadata: optional map[unknown]`

    - `timestamp: optional number`

  - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

    - `fusion_method: optional "rrf" or "max"`

      - `"rrf"`

      - `"max"`

    - `keyword_rank: optional number`

    - `keyword_score: optional number`

    - `reranking_score: optional number`

    - `vector_rank: optional number`

    - `vector_score: optional number`

- `id: optional string`

- `model: optional string`

- `object: optional string`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/chat/completions \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "messages": [
            {
              "content": "string",
              "role": "system"
            }
          ]
        }'
```

#### Response

```json
{
  "choices": [
    {
      "message": {
        "content": "string",
        "role": "system"
      },
      "index": 0
    }
  ],
  "chunks": [
    {
      "id": "id",
      "score": 0,
      "text": "text",
      "type": "type",
      "item": {
        "key": "key",
        "metadata": {
          "foo": "bar"
        },
        "timestamp": 0
      },
      "scoring_details": {
        "fusion_method": "rrf",
        "keyword_rank": 0,
        "keyword_score": 0,
        "reranking_score": 0,
        "vector_rank": 0,
        "vector_score": 0
      }
    }
  ],
  "id": "id",
  "model": "model",
  "object": "object"
}
```

## Domain Types

### Instance List Response

- `InstanceListResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Create Response

- `InstanceCreateResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Read Response

- `InstanceReadResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Update Response

- `InstanceUpdateResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Delete Response

- `InstanceDeleteResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Stats Response

- `InstanceStatsResponse object { completed, degraded, engine, 8 more }`

  - `completed: optional number`

  - `degraded: optional boolean`

    True when status counts are unavailable (e.g. legacy stats query exceeded D1 statement-size limit). Counts are omitted in this case.

  - `engine: optional object { r2, vectorize }`

    Engine-specific metadata. Present only for managed (v3) instances.

    - `r2: optional object { metadataSizeBytes, objectCount, payloadSizeBytes }`

      R2 bucket storage usage in bytes.

      - `metadataSizeBytes: number`

      - `objectCount: number`

      - `payloadSizeBytes: number`

    - `vectorize: optional object { dimensions, vectorsCount }`

      Vectorize index metadata (dimensions, vector count).

      - `dimensions: number`

      - `vectorsCount: number`

  - `error: optional number`

  - `file_embed_errors: optional map[unknown]`

  - `index_source_errors: optional map[unknown]`

  - `last_activity: optional string`

  - `outdated: optional number`

  - `queued: optional number`

  - `running: optional number`

  - `skipped: optional number`

### Instance Search Response

- `InstanceSearchResponse object { chunks, query_kind, search_query }`

  - `chunks: array of object { id, score, text, 3 more }`

    - `id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `query_kind: "text" or "image" or "multimodal"`

    - `"text"`

    - `"image"`

    - `"multimodal"`

  - `search_query: optional string`

### Instance Chat Completions Response

- `InstanceChatCompletionsResponse object { choices, chunks, id, 2 more }`

  - `choices: array of object { message, index }`

    - `message: object { content, role }`

      - `content: string or array of object { text, type }  or object { image_url, type }`

        - `string`

        - `array of object { text, type }  or object { image_url, type }`

          - `object { text, type }`

            - `text: string`

            - `type: "text"`

              - `"text"`

          - `object { image_url, type }`

            - `image_url: object { url }`

              - `url: string`

            - `type: "image_url"`

              - `"image_url"`

      - `role: "system" or "developer" or "user" or 2 more`

        - `"system"`

        - `"developer"`

        - `"user"`

        - `"assistant"`

        - `"tool"`

    - `index: optional number`

  - `chunks: array of object { id, score, text, 3 more }`

    - `id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `id: optional string`

  - `model: optional string`

  - `object: optional string`

# Jobs

## List Jobs

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/jobs`

Lists indexing jobs for an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Query Parameters

- `page: optional number`

- `per_page: optional number`

### Returns

- `result: array of object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/jobs \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "id",
      "source": "user",
      "description": "description",
      "end_reason": "end_reason",
      "ended_at": "ended_at",
      "last_seen_at": "last_seen_at",
      "started_at": "started_at"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Create new job

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/jobs`

Creates a new indexing job for an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `description: optional string`

### Returns

- `result: object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/jobs \
    -X POST \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "id",
    "source": "user",
    "description": "description",
    "end_reason": "end_reason",
    "ended_at": "ended_at",
    "last_seen_at": "last_seen_at",
    "started_at": "started_at"
  },
  "success": true
}
```

## Get a Job Details

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/jobs/{job_id}`

Retrieves details for a specific AI Search indexing job.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `job_id: string`

### Returns

- `result: object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/jobs/$JOB_ID \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "id",
    "source": "user",
    "description": "description",
    "end_reason": "end_reason",
    "ended_at": "ended_at",
    "last_seen_at": "last_seen_at",
    "started_at": "started_at"
  },
  "success": true
}
```

## Cancel an indexing job.

**patch** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/jobs/{job_id}`

Cancel an in-progress indexing job for an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `job_id: string`

### Body Parameters

- `action: "cancel"`

  - `"cancel"`

### Returns

- `result: object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/jobs/$JOB_ID \
    -X PATCH \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "action": "cancel"
        }'
```

#### Response

```json
{
  "result": {
    "id": "id",
    "source": "user",
    "description": "description",
    "end_reason": "end_reason",
    "ended_at": "ended_at",
    "last_seen_at": "last_seen_at",
    "started_at": "started_at"
  },
  "success": true
}
```

## List Job Logs

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/jobs/{job_id}/logs`

Lists log entries for an AI Search indexing job.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `job_id: string`

### Query Parameters

- `page: optional number`

- `per_page: optional number`

### Returns

- `result: array of object { id, created_at, message, message_type }`

  - `id: number`

  - `created_at: number`

  - `message: string`

  - `message_type: number`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/jobs/$JOB_ID/logs \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": 0,
      "created_at": 0,
      "message": "message",
      "message_type": 0
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Domain Types

### Job List Response

- `JobListResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Create Response

- `JobCreateResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Get Response

- `JobGetResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Update Response

- `JobUpdateResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Logs Response

- `JobLogsResponse = array of object { id, created_at, message, message_type }`

  - `id: number`

  - `created_at: number`

  - `message: string`

  - `message_type: number`

# Items

## Items List.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items`

Lists indexed items in an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Query Parameters

- `item_id: optional string`

  Filter items by their unique ID. Returns at most one item.

- `key: optional string`

  Filter items by their exact key (object key / filename). Keys are unique per source, so combine with `source` to disambiguate across data sources.

- `metadata_filter: optional string`

  JSON-encoded metadata filter using Vectorize filter syntax. Examples: {"folder":"reports/"}, {"timestamp":{"$gte":1700000000000}}, {"folder":{"$in":["docs/","reports/"]}}

- `page: optional number`

- `per_page: optional number`

- `search: optional string`

- `sort_by: optional "status" or "modified_at"`

  Sort order for items. "status" (default) sorts by status priority then last_seen_at. "modified_at" sorts by file modification time (most recent first), falling back to created_at.

  - `"status"`

  - `"modified_at"`

- `source: optional string`

  Filter items by source_id. Use "builtin" for uploaded files, or a source identifier like "web-crawler:https://example.com".

- `status: optional "queued" or "running" or "completed" or 3 more`

  - `"queued"`

  - `"running"`

  - `"completed"`

  - `"error"`

  - `"skipped"`

  - `"outdated"`

### Returns

- `result: array of object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

- `result_info: object { count, page, total_count, per_page }`

  - `count: number`

  - `page: number`

  - `total_count: number`

  - `per_page: optional number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "id",
      "checksum": "checksum",
      "chunks_count": 0,
      "created_at": "2019-12-27T18:11:19.117Z",
      "file_size": 0,
      "key": "key",
      "last_seen_at": "2019-12-27T18:11:19.117Z",
      "namespace": "namespace",
      "next_action": "INDEX",
      "source_id": "source_id",
      "status": "queued",
      "error": "error"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "total_count": 0,
    "per_page": 5
  },
  "success": true
}
```

## Upload Item.

**post** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items`

Uploads a file to a managed AI Search instance via multipart/form-data.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Returns

- `result: object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items \
    -H 'Content-Type: multipart/form-data' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -F file='{"file":"Example data"}'
```

#### Response

```json
{
  "result": {
    "id": "id",
    "checksum": "checksum",
    "chunks_count": 0,
    "created_at": "2019-12-27T18:11:19.117Z",
    "file_size": 0,
    "key": "key",
    "last_seen_at": "2019-12-27T18:11:19.117Z",
    "namespace": "namespace",
    "next_action": "INDEX",
    "source_id": "source_id",
    "status": "queued",
    "error": "error"
  },
  "success": true
}
```

## Create or Update Item.

**put** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items`

Creates or updates an indexed item in an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `key: string`

  Item key / filename. Must not exceed 128 characters.

- `next_action: "INDEX"`

  - `"INDEX"`

- `wait_for_completion: optional boolean`

  Wait for indexing to fully complete before responding. On RAGs with vector indexing enabled, this additionally waits for Vectorize ingestion confirmation (up to 40s) so the returned item reflects a queryable state. On timeout the item is returned in `running` state and the background alarm continues polling. Defaults to false.

### Returns

- `result: object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items \
    -X PUT \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "key": "key",
          "next_action": "INDEX"
        }'
```

#### Response

```json
{
  "result": {
    "id": "id",
    "checksum": "checksum",
    "chunks_count": 0,
    "created_at": "2019-12-27T18:11:19.117Z",
    "file_size": 0,
    "key": "key",
    "last_seen_at": "2019-12-27T18:11:19.117Z",
    "namespace": "namespace",
    "next_action": "INDEX",
    "source_id": "source_id",
    "status": "queued",
    "error": "error"
  },
  "success": true
}
```

## Get Item.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items/{item_id}`

Retrieves a specific indexed item from an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `item_id: string`

### Returns

- `result: object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items/$ITEM_ID \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "id",
    "checksum": "checksum",
    "chunks_count": 0,
    "created_at": "2019-12-27T18:11:19.117Z",
    "file_size": 0,
    "key": "key",
    "last_seen_at": "2019-12-27T18:11:19.117Z",
    "namespace": "namespace",
    "next_action": "INDEX",
    "source_id": "source_id",
    "status": "queued",
    "error": "error"
  },
  "success": true
}
```

## Sync Item.

**patch** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items/{item_id}`

Syncs an item to an AI Search instance index.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `item_id: string`

### Body Parameters

- `next_action: "INDEX"`

  - `"INDEX"`

- `wait_for_completion: optional boolean`

  Wait for indexing to fully complete before responding. On RAGs with vector indexing enabled, this additionally waits for Vectorize ingestion confirmation (up to 40s) so the returned item reflects a queryable state. On timeout the item is returned in `running` state and the background alarm continues polling. Defaults to false.

### Returns

- `result: object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items/$ITEM_ID \
    -X PATCH \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "next_action": "INDEX"
        }'
```

#### Response

```json
{
  "result": {
    "id": "id",
    "checksum": "checksum",
    "chunks_count": 0,
    "created_at": "2019-12-27T18:11:19.117Z",
    "file_size": 0,
    "key": "key",
    "last_seen_at": "2019-12-27T18:11:19.117Z",
    "namespace": "namespace",
    "next_action": "INDEX",
    "source_id": "source_id",
    "status": "queued",
    "error": "error"
  },
  "success": true
}
```

## Delete Item.

**delete** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items/{item_id}`

Deletes a file from a managed AI Search instance and triggers a reindex.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `item_id: string`

### Returns

- `result: object { key }`

  - `key: string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items/$ITEM_ID \
    -X DELETE \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "key": "key"
  },
  "success": true
}
```

## Download Item Content.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items/{item_id}/download`

Downloads the raw file content for a specific item from the managed AI Search instance storage.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `item_id: string`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items/$ITEM_ID/download \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

## Item Logs.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items/{item_id}/logs`

Lists processing logs for a specific item in an AI Search instance.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `item_id: string`

### Query Parameters

- `cursor: optional string`

- `limit: optional number`

### Returns

- `result: array of object { action, chunkCount, errorType, 4 more }`

  - `action: string`

  - `chunkCount: number`

  - `errorType: string`

  - `fileKey: string`

  - `message: string`

  - `processingTimeMs: number`

  - `timestamp: string`

- `result_info: object { count, cursor, per_page, truncated }`

  - `count: number`

  - `cursor: string`

  - `per_page: number`

  - `truncated: boolean`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items/$ITEM_ID/logs \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "action": "action",
      "chunkCount": 0,
      "errorType": "errorType",
      "fileKey": "fileKey",
      "message": "message",
      "processingTimeMs": 0,
      "timestamp": "2019-12-27T18:11:19.117Z"
    }
  ],
  "result_info": {
    "count": 0,
    "cursor": "cursor",
    "per_page": 0,
    "truncated": true
  },
  "success": true
}
```

## List Item Chunks.

**get** `/accounts/{account_id}/ai-search/namespaces/{name}/instances/{id}/items/{item_id}/chunks`

Lists chunks for a specific item in an AI Search instance, including their text content.

### Path Parameters

- `account_id: string`

- `name: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `item_id: string`

### Query Parameters

- `limit: optional number`

- `offset: optional number`

### Returns

- `result: array of object { id, item, text, 2 more }`

  - `id: string`

  - `item: object { key, metadata, timestamp }`

    - `key: string`

    - `metadata: optional map[unknown]`

    - `timestamp: optional number`

  - `text: string`

  - `end_byte: optional number`

  - `start_byte: optional number`

- `result_info: object { count, limit, offset, total }`

  - `count: number`

  - `limit: number`

  - `offset: number`

  - `total: number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/namespaces/$NAME/instances/$ID/items/$ITEM_ID/chunks \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "id",
      "item": {
        "key": "key",
        "metadata": {
          "foo": "bar"
        },
        "timestamp": 0
      },
      "text": "text",
      "end_byte": 0,
      "start_byte": 0
    }
  ],
  "result_info": {
    "count": 0,
    "limit": 0,
    "offset": 0,
    "total": 0
  },
  "success": true
}
```

## Domain Types

### Item List Response

- `ItemListResponse object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

### Item Upload Response

- `ItemUploadResponse object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

### Item Create Or Update Response

- `ItemCreateOrUpdateResponse object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

### Item Get Response

- `ItemGetResponse object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

### Item Sync Response

- `ItemSyncResponse object { id, checksum, chunks_count, 9 more }`

  - `id: string`

  - `checksum: string`

  - `chunks_count: number`

  - `created_at: string`

  - `file_size: number`

  - `key: string`

  - `last_seen_at: string`

  - `namespace: string`

  - `next_action: "INDEX" or "DELETE"`

    - `"INDEX"`

    - `"DELETE"`

  - `source_id: string`

    Identifies which data source this item belongs to. "builtin" for uploaded files, "{type}:{source}" for external sources, null for legacy items.

  - `status: "queued" or "running" or "completed" or 3 more`

    - `"queued"`

    - `"running"`

    - `"completed"`

    - `"error"`

    - `"skipped"`

    - `"outdated"`

  - `error: optional string`

### Item Delete Response

- `ItemDeleteResponse object { key }`

  - `key: string`

### Item Logs Response

- `ItemLogsResponse = array of object { action, chunkCount, errorType, 4 more }`

  - `action: string`

  - `chunkCount: number`

  - `errorType: string`

  - `fileKey: string`

  - `message: string`

  - `processingTimeMs: number`

  - `timestamp: string`

### Item Chunks Response

- `ItemChunksResponse = array of object { id, item, text, 2 more }`

  - `id: string`

  - `item: object { key, metadata, timestamp }`

    - `key: string`

    - `metadata: optional map[unknown]`

    - `timestamp: optional number`

  - `text: string`

  - `end_byte: optional number`

  - `start_byte: optional number`

# Instances

## List AI Search instances.

**get** `/accounts/{account_id}/ai-search/instances`

List all AI Search instances in the account.

### Path Parameters

- `account_id: string`

### Query Parameters

- `namespace: optional string`

  Filter by namespace.

- `order_by: optional "created_at"`

  Field to order results by.

  - `"created_at"`

- `order_by_direction: optional "asc" or "desc"`

  Order direction.

  - `"asc"`

  - `"desc"`

- `page: optional number`

  Page number (1-indexed).

- `per_page: optional number`

  Number of results per page.

- `search: optional string`

  Filter instances whose id contains this string (case-insensitive).

### Returns

- `result: array of object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "my-ai-search",
      "created_at": "2019-12-27T18:11:19.117Z",
      "modified_at": "2019-12-27T18:11:19.117Z",
      "ai_gateway_id": "ai_gateway_id",
      "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
      "cache": true,
      "cache_threshold": "super_strict_match",
      "cache_ttl": 600,
      "chunk_overlap": 0,
      "chunk_size": 64,
      "created_by": "created_by",
      "custom_metadata": [
        {
          "data_type": "text",
          "field_name": "x"
        }
      ],
      "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
      "enable": true,
      "engine_version": 0,
      "fusion_method": "max",
      "hybrid_search_enabled": true,
      "index_method": {
        "keyword": true,
        "vector": true
      },
      "indexing_options": {
        "keyword_tokenizer": "porter"
      },
      "last_activity": "2019-12-27T18:11:19.117Z",
      "max_num_results": 1,
      "metadata": {
        "created_from_aisearch_wizard": true,
        "worker_domain": "worker_domain"
      },
      "modified_by": "modified_by",
      "namespace": "namespace",
      "paused": true,
      "public_endpoint_id": "public_endpoint_id",
      "public_endpoint_params": {
        "authorized_hosts": [
          "string"
        ],
        "chat_completions_endpoint": {
          "disabled": true
        },
        "custom_domains": [
          "search.example.com"
        ],
        "enabled": true,
        "mcp": {
          "description": "description",
          "disabled": true
        },
        "rate_limit": {
          "period_ms": 60000,
          "requests": 1,
          "technique": "fixed"
        },
        "search_endpoint": {
          "disabled": true
        }
      },
      "reranking": true,
      "reranking_model": "@cf/baai/bge-reranker-base",
      "retrieval_options": {
        "boost_by": [
          {
            "field": "timestamp",
            "direction": "desc"
          }
        ],
        "keyword_match_mode": "and"
      },
      "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
      "rewrite_query": true,
      "score_threshold": 0,
      "source": "source",
      "source_params": {
        "exclude_items": [
          "/admin/**",
          "/private/**",
          "**\\temp\\**"
        ],
        "include_items": [
          "/blog/**",
          "/docs/**/*.html",
          "**\\blog\\**.html"
        ],
        "prefix": "prefix",
        "r2_jurisdiction": "r2_jurisdiction",
        "web_crawler": {
          "parse_options": {
            "content_selector": [
              {
                "path": "**/blog/**",
                "selector": "article div.post-body"
              },
              {
                "path": "**/docs/**",
                "selector": "main"
              }
            ],
            "include_headers": {
              "cache-control": "no-cache, no-store"
            },
            "include_images": true,
            "specific_sitemaps": [
              "https://example.com/sitemap.xml",
              "https://example.com/blog-sitemap.xml"
            ],
            "use_browser_rendering": true
          },
          "parse_type": "sitemap"
        }
      },
      "status": "status",
      "sync_interval": 900,
      "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
      "type": "r2"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Create an AI Search instance.

**post** `/accounts/{account_id}/ai-search/instances`

Create a new AI Search instance with the given configuration.

### Path Parameters

- `account_id: string`

### Body Parameters

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `ai_gateway_id: optional string`

- `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `cache: optional boolean`

- `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

  - `"super_strict_match"`

  - `"close_enough"`

  - `"flexible_friend"`

  - `"anything_goes"`

- `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

  Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

  - `600`

  - `1800`

  - `3600`

  - `7200`

  - `21600`

  - `43200`

  - `86400`

  - `172800`

  - `259200`

  - `518400`

- `chunk: optional boolean`

- `chunk_overlap: optional number`

- `chunk_size: optional number`

- `custom_metadata: optional array of object { data_type, field_name }`

  - `data_type: "text" or "number" or "boolean" or "datetime"`

    - `"text"`

    - `"number"`

    - `"boolean"`

    - `"datetime"`

  - `field_name: string`

- `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

  - `"@cf/qwen/qwen3-embedding-0.6b"`

  - `"@cf/qwen/qwen3-vl-embedding-2b"`

  - `"@cf/baai/bge-m3"`

  - `"@cf/baai/bge-large-en-v1.5"`

  - `"@cf/google/embeddinggemma-300m"`

  - `"google-ai-studio/gemini-embedding-001"`

  - `"google-ai-studio/gemini-embedding-2-preview"`

  - `"google-ai-studio/gemini-embedding-2"`

  - `"openai/text-embedding-3-small"`

  - `"openai/text-embedding-3-large"`

  - `""`

- `fusion_method: optional "max" or "rrf"`

  - `"max"`

  - `"rrf"`

- `hybrid_search_enabled: optional boolean`

  Deprecated — use index_method instead.

- `index_method: optional object { keyword, vector }`

  Controls which storage backends are used during indexing. Defaults to vector-only.

  - `keyword: boolean`

    Enable keyword (BM25) storage backend.

  - `vector: boolean`

    Enable vector (embedding) storage backend.

- `indexing_options: optional object { keyword_tokenizer }`

  - `keyword_tokenizer: optional "porter" or "trigram"`

    Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

    - `"porter"`

    - `"trigram"`

- `max_num_results: optional number`

- `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

  - `created_from_aisearch_wizard: optional boolean`

  - `worker_domain: optional string`

- `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

  - `authorized_hosts: optional array of string`

  - `chat_completions_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable chat completions endpoint for this public endpoint

  - `custom_domains: optional array of string`

    Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

  - `enabled: optional boolean`

  - `mcp: optional object { description, disabled }`

    - `description: optional string`

    - `disabled: optional boolean`

      Disable MCP endpoint for this public endpoint

  - `rate_limit: optional object { period_ms, requests, technique }`

    - `period_ms: optional number`

    - `requests: optional number`

    - `technique: optional "fixed" or "sliding"`

      - `"fixed"`

      - `"sliding"`

  - `search_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable search endpoint for this public endpoint

- `reranking: optional boolean`

- `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

  - `"@cf/baai/bge-reranker-base"`

  - `""`

- `retrieval_options: optional object { boost_by, keyword_match_mode }`

  - `boost_by: optional array of object { field, direction }`

    Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

    - `field: string`

      Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

    - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

      Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

      - `"asc"`

      - `"desc"`

      - `"exists"`

      - `"not_exists"`

  - `keyword_match_mode: optional "and" or "or"`

    Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

    - `"and"`

    - `"or"`

- `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `rewrite_query: optional boolean`

- `score_threshold: optional number`

- `source: optional string`

- `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

  - `exclude_items: optional array of string`

    List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

  - `include_items: optional array of string`

    List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

  - `prefix: optional string`

  - `r2_jurisdiction: optional string`

  - `web_crawler: optional object { parse_options, parse_type }`

    - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

      - `content_selector: optional array of object { path, selector }`

        List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

        - `path: string`

          Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

        - `selector: string`

          CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

      - `include_headers: optional map[string]`

        Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

      - `include_images: optional boolean`

      - `specific_sitemaps: optional array of string`

        List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

      - `use_browser_rendering: optional boolean`

    - `parse_type: optional "sitemap" or "crawl"`

      - `"sitemap"`

      - `"crawl"`

- `sync_interval: optional 900 or 1800 or 3600 or 5 more`

  Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

  - `900`

  - `1800`

  - `3600`

  - `7200`

  - `14400`

  - `21600`

  - `43200`

  - `86400`

- `token_id: optional string`

- `type: optional "r2" or "web-crawler"`

  - `"r2"`

  - `"web-crawler"`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "id": "my-ai-search"
        }'
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Get an AI Search instance.

**get** `/accounts/{account_id}/ai-search/instances/{id}`

Retrieve the configuration and status of an AI Search instance.

### Path Parameters

- `account_id: string`

- `id: string`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Update an AI Search instance.

**put** `/accounts/{account_id}/ai-search/instances/{id}`

Update the configuration of an AI Search instance.

### Path Parameters

- `account_id: string`

- `id: string`

### Body Parameters

- `ai_gateway_id: optional string`

- `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `cache: optional boolean`

- `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

  - `"super_strict_match"`

  - `"close_enough"`

  - `"flexible_friend"`

  - `"anything_goes"`

- `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

  Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

  - `600`

  - `1800`

  - `3600`

  - `7200`

  - `21600`

  - `43200`

  - `86400`

  - `172800`

  - `259200`

  - `518400`

- `chunk: optional boolean`

- `chunk_overlap: optional number`

- `chunk_size: optional number`

- `custom_metadata: optional array of object { data_type, field_name }`

  - `data_type: "text" or "number" or "boolean" or "datetime"`

    - `"text"`

    - `"number"`

    - `"boolean"`

    - `"datetime"`

  - `field_name: string`

- `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

  - `"@cf/qwen/qwen3-embedding-0.6b"`

  - `"@cf/qwen/qwen3-vl-embedding-2b"`

  - `"@cf/baai/bge-m3"`

  - `"@cf/baai/bge-large-en-v1.5"`

  - `"@cf/google/embeddinggemma-300m"`

  - `"google-ai-studio/gemini-embedding-001"`

  - `"google-ai-studio/gemini-embedding-2-preview"`

  - `"google-ai-studio/gemini-embedding-2"`

  - `"openai/text-embedding-3-small"`

  - `"openai/text-embedding-3-large"`

  - `""`

- `fusion_method: optional "max" or "rrf"`

  - `"max"`

  - `"rrf"`

- `index_method: optional object { keyword, vector }`

  Controls which storage backends are used during indexing. Defaults to vector-only.

  - `keyword: boolean`

    Enable keyword (BM25) storage backend.

  - `vector: boolean`

    Enable vector (embedding) storage backend.

- `indexing_options: optional object { keyword_tokenizer }`

  - `keyword_tokenizer: optional "porter" or "trigram"`

    Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

    - `"porter"`

    - `"trigram"`

- `max_num_results: optional number`

- `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

  - `created_from_aisearch_wizard: optional boolean`

  - `worker_domain: optional string`

- `paused: optional boolean`

- `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

  - `authorized_hosts: optional array of string`

  - `chat_completions_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable chat completions endpoint for this public endpoint

  - `custom_domains: optional array of string`

    Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

  - `enabled: optional boolean`

  - `mcp: optional object { description, disabled }`

    - `description: optional string`

    - `disabled: optional boolean`

      Disable MCP endpoint for this public endpoint

  - `rate_limit: optional object { period_ms, requests, technique }`

    - `period_ms: optional number`

    - `requests: optional number`

    - `technique: optional "fixed" or "sliding"`

      - `"fixed"`

      - `"sliding"`

  - `search_endpoint: optional object { disabled }`

    - `disabled: optional boolean`

      Disable search endpoint for this public endpoint

- `reranking: optional boolean`

- `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

  - `"@cf/baai/bge-reranker-base"`

  - `""`

- `retrieval_options: optional object { boost_by, keyword_match_mode }`

  - `boost_by: optional array of object { field, direction }`

    Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

    - `field: string`

      Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

    - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

      Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

      - `"asc"`

      - `"desc"`

      - `"exists"`

      - `"not_exists"`

  - `keyword_match_mode: optional "and" or "or"`

    Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

    - `"and"`

    - `"or"`

- `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `rewrite_query: optional boolean`

- `score_threshold: optional number`

- `source: optional string`

- `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

  - `exclude_items: optional array of string`

    List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

  - `include_items: optional array of string`

    List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

  - `prefix: optional string`

  - `r2_jurisdiction: optional string`

  - `web_crawler: optional object { parse_options, parse_type }`

    - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

      - `content_selector: optional array of object { path, selector }`

        List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

        - `path: string`

          Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

        - `selector: string`

          CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

      - `include_headers: optional map[string]`

        Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

      - `include_images: optional boolean`

      - `specific_sitemaps: optional array of string`

        List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

      - `use_browser_rendering: optional boolean`

    - `parse_type: optional "sitemap" or "crawl"`

      - `"sitemap"`

      - `"crawl"`

- `summarization: optional boolean`

- `summarization_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `sync_interval: optional 900 or 1800 or 3600 or 5 more`

  Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

  - `900`

  - `1800`

  - `3600`

  - `7200`

  - `14400`

  - `21600`

  - `43200`

  - `86400`

- `system_prompt_ai_search: optional string`

- `system_prompt_index_summarization: optional string`

- `system_prompt_rewrite_query: optional string`

- `token_id: optional string`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID \
    -X PUT \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Delete an AI Search instance.

**delete** `/accounts/{account_id}/ai-search/instances/{id}`

Permanently delete an AI Search instance and all its indexed data.

### Path Parameters

- `account_id: string`

- `id: string`

### Returns

- `result: object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID \
    -X DELETE \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "my-ai-search",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "ai_gateway_id": "ai_gateway_id",
    "ai_search_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "cache": true,
    "cache_threshold": "super_strict_match",
    "cache_ttl": 600,
    "chunk_overlap": 0,
    "chunk_size": 64,
    "created_by": "created_by",
    "custom_metadata": [
      {
        "data_type": "text",
        "field_name": "x"
      }
    ],
    "embedding_model": "@cf/qwen/qwen3-embedding-0.6b",
    "enable": true,
    "engine_version": 0,
    "fusion_method": "max",
    "hybrid_search_enabled": true,
    "index_method": {
      "keyword": true,
      "vector": true
    },
    "indexing_options": {
      "keyword_tokenizer": "porter"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "max_num_results": 1,
    "metadata": {
      "created_from_aisearch_wizard": true,
      "worker_domain": "worker_domain"
    },
    "modified_by": "modified_by",
    "namespace": "namespace",
    "paused": true,
    "public_endpoint_id": "public_endpoint_id",
    "public_endpoint_params": {
      "authorized_hosts": [
        "string"
      ],
      "chat_completions_endpoint": {
        "disabled": true
      },
      "custom_domains": [
        "search.example.com"
      ],
      "enabled": true,
      "mcp": {
        "description": "description",
        "disabled": true
      },
      "rate_limit": {
        "period_ms": 60000,
        "requests": 1,
        "technique": "fixed"
      },
      "search_endpoint": {
        "disabled": true
      }
    },
    "reranking": true,
    "reranking_model": "@cf/baai/bge-reranker-base",
    "retrieval_options": {
      "boost_by": [
        {
          "field": "timestamp",
          "direction": "desc"
        }
      ],
      "keyword_match_mode": "and"
    },
    "rewrite_model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "rewrite_query": true,
    "score_threshold": 0,
    "source": "source",
    "source_params": {
      "exclude_items": [
        "/admin/**",
        "/private/**",
        "**\\temp\\**"
      ],
      "include_items": [
        "/blog/**",
        "/docs/**/*.html",
        "**\\blog\\**.html"
      ],
      "prefix": "prefix",
      "r2_jurisdiction": "r2_jurisdiction",
      "web_crawler": {
        "parse_options": {
          "content_selector": [
            {
              "path": "**/blog/**",
              "selector": "article div.post-body"
            },
            {
              "path": "**/docs/**",
              "selector": "main"
            }
          ],
          "include_headers": {
            "cache-control": "no-cache, no-store"
          },
          "include_images": true,
          "specific_sitemaps": [
            "https://example.com/sitemap.xml",
            "https://example.com/blog-sitemap.xml"
          ],
          "use_browser_rendering": true
        },
        "parse_type": "sitemap"
      }
    },
    "status": "status",
    "sync_interval": 900,
    "token_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "type": "r2"
  },
  "success": true
}
```

## Get instance statistics.

**get** `/accounts/{account_id}/ai-search/instances/{id}/stats`

Retrieve usage and indexing statistics for an AI Search instance.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Returns

- `result: object { completed, degraded, engine, 8 more }`

  - `completed: optional number`

  - `degraded: optional boolean`

    True when status counts are unavailable (e.g. legacy stats query exceeded D1 statement-size limit). Counts are omitted in this case.

  - `engine: optional object { r2, vectorize }`

    Engine-specific metadata. Present only for managed (v3) instances.

    - `r2: optional object { metadataSizeBytes, objectCount, payloadSizeBytes }`

      R2 bucket storage usage in bytes.

      - `metadataSizeBytes: number`

      - `objectCount: number`

      - `payloadSizeBytes: number`

    - `vectorize: optional object { dimensions, vectorsCount }`

      Vectorize index metadata (dimensions, vector count).

      - `dimensions: number`

      - `vectorsCount: number`

  - `error: optional number`

  - `file_embed_errors: optional map[unknown]`

  - `index_source_errors: optional map[unknown]`

  - `last_activity: optional string`

  - `outdated: optional number`

  - `queued: optional number`

  - `running: optional number`

  - `skipped: optional number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/stats \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "completed": 0,
    "degraded": true,
    "engine": {
      "r2": {
        "metadataSizeBytes": 0,
        "objectCount": 0,
        "payloadSizeBytes": 0
      },
      "vectorize": {
        "dimensions": 0,
        "vectorsCount": 0
      }
    },
    "error": 0,
    "file_embed_errors": {
      "foo": "bar"
    },
    "index_source_errors": {
      "foo": "bar"
    },
    "last_activity": "2019-12-27T18:11:19.117Z",
    "outdated": 0,
    "queued": 0,
    "running": 0,
    "skipped": 0
  },
  "success": true
}
```

## Search

**post** `/accounts/{account_id}/ai-search/instances/{id}/search`

Executes a semantic search query against an AI Search instance to find relevant indexed content.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `ai_search_options: optional object { cache, query_rewrite, reranking, retrieval }`

  - `cache: optional object { cache_threshold, enabled }`

    - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

      - `"super_strict_match"`

      - `"close_enough"`

      - `"flexible_friend"`

      - `"anything_goes"`

    - `enabled: optional boolean`

  - `query_rewrite: optional object { enabled, model, rewrite_prompt }`

    - `enabled: optional boolean`

    - `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

      - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

      - `"@cf/zai-org/glm-4.7-flash"`

      - `"@cf/meta/llama-3.1-8b-instruct-fast"`

      - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

      - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

      - `"@cf/qwen/qwen3-30b-a3b-fp8"`

      - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

      - `"@cf/moonshotai/kimi-k2-instruct"`

      - `"@cf/google/gemma-3-12b-it"`

      - `"@cf/google/gemma-4-26b-a4b-it"`

      - `"@cf/moonshotai/kimi-k2.5"`

      - `"anthropic/claude-3-7-sonnet"`

      - `"anthropic/claude-sonnet-4"`

      - `"anthropic/claude-opus-4"`

      - `"anthropic/claude-3-5-haiku"`

      - `"cerebras/qwen-3-235b-a22b-instruct"`

      - `"cerebras/qwen-3-235b-a22b-thinking"`

      - `"cerebras/llama-3.3-70b"`

      - `"cerebras/llama-4-maverick-17b-128e-instruct"`

      - `"cerebras/llama-4-scout-17b-16e-instruct"`

      - `"cerebras/gpt-oss-120b"`

      - `"google-ai-studio/gemini-2.5-flash"`

      - `"google-ai-studio/gemini-2.5-pro"`

      - `"grok/grok-4"`

      - `"groq/llama-3.3-70b-versatile"`

      - `"groq/llama-3.1-8b-instant"`

      - `"openai/gpt-5"`

      - `"openai/gpt-5-mini"`

      - `"openai/gpt-5-nano"`

      - `""`

    - `rewrite_prompt: optional string`

  - `reranking: optional object { enabled, match_threshold, model }`

    - `enabled: optional boolean`

    - `match_threshold: optional number`

    - `model: optional "@cf/baai/bge-reranker-base" or ""`

      - `"@cf/baai/bge-reranker-base"`

      - `""`

  - `retrieval: optional object { boost_by, context_expansion, filters, 6 more }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Overrides the instance-level boost_by config. Direction defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `context_expansion: optional number`

    - `filters: optional map[unknown]`

    - `fusion_method: optional "max" or "rrf"`

      - `"max"`

      - `"rrf"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted, falls back to the instance-level retrieval_options.keyword_match_mode, then to 'and'.

      - `"and"`

      - `"or"`

    - `match_threshold: optional number`

    - `max_num_results: optional number`

    - `retrieval_type: optional "vector" or "keyword" or "hybrid"`

      - `"vector"`

      - `"keyword"`

      - `"hybrid"`

    - `return_on_failure: optional boolean`

- `messages: optional array of object { content, role }`

  OpenAI-compatible message array. For multimodal queries, set the last user message's `content` to an array of typed parts: `[{type:'text', text:'…'}, {type:'image_url', image_url:{url:'…'}}]`. Image inputs require the RAG's embedding_model to declare 'image' in supported_modalities.

  - `content: string or array of object { text, type }  or object { image_url, type }`

    - `string`

    - `array of object { text, type }  or object { image_url, type }`

      - `object { text, type }`

        - `text: string`

        - `type: "text"`

          - `"text"`

      - `object { image_url, type }`

        - `image_url: object { url }`

          - `url: string`

        - `type: "image_url"`

          - `"image_url"`

  - `role: "system" or "developer" or "user" or 2 more`

    - `"system"`

    - `"developer"`

    - `"user"`

    - `"assistant"`

    - `"tool"`

- `query: optional string`

  A simple text query string. Alternative to 'messages' — provide either this or 'messages', not both.

### Returns

- `result: object { chunks, query_kind, search_query }`

  - `chunks: array of object { id, score, text, 3 more }`

    - `id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `query_kind: "text" or "image" or "multimodal"`

    - `"text"`

    - `"image"`

    - `"multimodal"`

  - `search_query: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/search \
    -X POST \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "chunks": [
      {
        "id": "id",
        "score": 0,
        "text": "text",
        "type": "type",
        "item": {
          "key": "key",
          "metadata": {
            "foo": "bar"
          },
          "timestamp": 0
        },
        "scoring_details": {
          "fusion_method": "rrf",
          "keyword_rank": 0,
          "keyword_score": 0,
          "reranking_score": 0,
          "vector_rank": 0,
          "vector_score": 0
        }
      }
    ],
    "query_kind": "text",
    "search_query": "search_query"
  },
  "success": true
}
```

## Chat Completions

**post** `/accounts/{account_id}/ai-search/instances/{id}/chat/completions`

Performs a chat completion request against an AI Search instance, using indexed content as context for generating responses.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `messages: array of object { content, role }`

  - `content: string or array of object { text, type }  or object { image_url, type }`

    - `string`

    - `array of object { text, type }  or object { image_url, type }`

      - `object { text, type }`

        - `text: string`

        - `type: "text"`

          - `"text"`

      - `object { image_url, type }`

        - `image_url: object { url }`

          - `url: string`

        - `type: "image_url"`

          - `"image_url"`

  - `role: "system" or "developer" or "user" or 2 more`

    - `"system"`

    - `"developer"`

    - `"user"`

    - `"assistant"`

    - `"tool"`

- `ai_search_options: optional object { cache, query_rewrite, reranking, retrieval }`

  - `cache: optional object { cache_threshold, enabled }`

    - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

      - `"super_strict_match"`

      - `"close_enough"`

      - `"flexible_friend"`

      - `"anything_goes"`

    - `enabled: optional boolean`

  - `query_rewrite: optional object { enabled, model, rewrite_prompt }`

    - `enabled: optional boolean`

    - `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

      - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

      - `"@cf/zai-org/glm-4.7-flash"`

      - `"@cf/meta/llama-3.1-8b-instruct-fast"`

      - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

      - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

      - `"@cf/qwen/qwen3-30b-a3b-fp8"`

      - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

      - `"@cf/moonshotai/kimi-k2-instruct"`

      - `"@cf/google/gemma-3-12b-it"`

      - `"@cf/google/gemma-4-26b-a4b-it"`

      - `"@cf/moonshotai/kimi-k2.5"`

      - `"anthropic/claude-3-7-sonnet"`

      - `"anthropic/claude-sonnet-4"`

      - `"anthropic/claude-opus-4"`

      - `"anthropic/claude-3-5-haiku"`

      - `"cerebras/qwen-3-235b-a22b-instruct"`

      - `"cerebras/qwen-3-235b-a22b-thinking"`

      - `"cerebras/llama-3.3-70b"`

      - `"cerebras/llama-4-maverick-17b-128e-instruct"`

      - `"cerebras/llama-4-scout-17b-16e-instruct"`

      - `"cerebras/gpt-oss-120b"`

      - `"google-ai-studio/gemini-2.5-flash"`

      - `"google-ai-studio/gemini-2.5-pro"`

      - `"grok/grok-4"`

      - `"groq/llama-3.3-70b-versatile"`

      - `"groq/llama-3.1-8b-instant"`

      - `"openai/gpt-5"`

      - `"openai/gpt-5-mini"`

      - `"openai/gpt-5-nano"`

      - `""`

    - `rewrite_prompt: optional string`

  - `reranking: optional object { enabled, match_threshold, model }`

    - `enabled: optional boolean`

    - `match_threshold: optional number`

    - `model: optional "@cf/baai/bge-reranker-base" or ""`

      - `"@cf/baai/bge-reranker-base"`

      - `""`

  - `retrieval: optional object { boost_by, context_expansion, filters, 6 more }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Overrides the instance-level boost_by config. Direction defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `context_expansion: optional number`

    - `filters: optional map[unknown]`

    - `fusion_method: optional "max" or "rrf"`

      - `"max"`

      - `"rrf"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted, falls back to the instance-level retrieval_options.keyword_match_mode, then to 'and'.

      - `"and"`

      - `"or"`

    - `match_threshold: optional number`

    - `max_num_results: optional number`

    - `retrieval_type: optional "vector" or "keyword" or "hybrid"`

      - `"vector"`

      - `"keyword"`

      - `"hybrid"`

    - `return_on_failure: optional boolean`

- `model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

  - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

  - `"@cf/zai-org/glm-4.7-flash"`

  - `"@cf/meta/llama-3.1-8b-instruct-fast"`

  - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

  - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

  - `"@cf/qwen/qwen3-30b-a3b-fp8"`

  - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

  - `"@cf/moonshotai/kimi-k2-instruct"`

  - `"@cf/google/gemma-3-12b-it"`

  - `"@cf/google/gemma-4-26b-a4b-it"`

  - `"@cf/moonshotai/kimi-k2.5"`

  - `"anthropic/claude-3-7-sonnet"`

  - `"anthropic/claude-sonnet-4"`

  - `"anthropic/claude-opus-4"`

  - `"anthropic/claude-3-5-haiku"`

  - `"cerebras/qwen-3-235b-a22b-instruct"`

  - `"cerebras/qwen-3-235b-a22b-thinking"`

  - `"cerebras/llama-3.3-70b"`

  - `"cerebras/llama-4-maverick-17b-128e-instruct"`

  - `"cerebras/llama-4-scout-17b-16e-instruct"`

  - `"cerebras/gpt-oss-120b"`

  - `"google-ai-studio/gemini-2.5-flash"`

  - `"google-ai-studio/gemini-2.5-pro"`

  - `"grok/grok-4"`

  - `"groq/llama-3.3-70b-versatile"`

  - `"groq/llama-3.1-8b-instant"`

  - `"openai/gpt-5"`

  - `"openai/gpt-5-mini"`

  - `"openai/gpt-5-nano"`

  - `""`

- `stream: optional boolean`

### Returns

- `choices: array of object { message, index }`

  - `message: object { content, role }`

    - `content: string or array of object { text, type }  or object { image_url, type }`

      - `string`

      - `array of object { text, type }  or object { image_url, type }`

        - `object { text, type }`

          - `text: string`

          - `type: "text"`

            - `"text"`

        - `object { image_url, type }`

          - `image_url: object { url }`

            - `url: string`

          - `type: "image_url"`

            - `"image_url"`

    - `role: "system" or "developer" or "user" or 2 more`

      - `"system"`

      - `"developer"`

      - `"user"`

      - `"assistant"`

      - `"tool"`

  - `index: optional number`

- `chunks: array of object { id, score, text, 3 more }`

  - `id: string`

  - `score: number`

  - `text: string`

  - `type: string`

  - `item: optional object { key, metadata, timestamp }`

    - `key: string`

    - `metadata: optional map[unknown]`

    - `timestamp: optional number`

  - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

    - `fusion_method: optional "rrf" or "max"`

      - `"rrf"`

      - `"max"`

    - `keyword_rank: optional number`

    - `keyword_score: optional number`

    - `reranking_score: optional number`

    - `vector_rank: optional number`

    - `vector_score: optional number`

- `id: optional string`

- `model: optional string`

- `object: optional string`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/chat/completions \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "messages": [
            {
              "content": "string",
              "role": "system"
            }
          ]
        }'
```

#### Response

```json
{
  "choices": [
    {
      "message": {
        "content": "string",
        "role": "system"
      },
      "index": 0
    }
  ],
  "chunks": [
    {
      "id": "id",
      "score": 0,
      "text": "text",
      "type": "type",
      "item": {
        "key": "key",
        "metadata": {
          "foo": "bar"
        },
        "timestamp": 0
      },
      "scoring_details": {
        "fusion_method": "rrf",
        "keyword_rank": 0,
        "keyword_score": 0,
        "reranking_score": 0,
        "vector_rank": 0,
        "vector_score": 0
      }
    }
  ],
  "id": "id",
  "model": "model",
  "object": "object"
}
```

## Domain Types

### Instance List Response

- `InstanceListResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Create Response

- `InstanceCreateResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Read Response

- `InstanceReadResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Update Response

- `InstanceUpdateResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Delete Response

- `InstanceDeleteResponse object { id, created_at, modified_at, 36 more }`

  - `id: string`

    AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

  - `created_at: string`

  - `modified_at: string`

  - `ai_gateway_id: optional string`

  - `ai_search_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `cache: optional boolean`

  - `cache_threshold: optional "super_strict_match" or "close_enough" or "flexible_friend" or "anything_goes"`

    - `"super_strict_match"`

    - `"close_enough"`

    - `"flexible_friend"`

    - `"anything_goes"`

  - `cache_ttl: optional 600 or 1800 or 3600 or 7 more`

    Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).

    - `600`

    - `1800`

    - `3600`

    - `7200`

    - `21600`

    - `43200`

    - `86400`

    - `172800`

    - `259200`

    - `518400`

  - `chunk_overlap: optional number`

  - `chunk_size: optional number`

  - `created_by: optional string`

  - `custom_metadata: optional array of object { data_type, field_name }`

    - `data_type: "text" or "number" or "boolean" or "datetime"`

      - `"text"`

      - `"number"`

      - `"boolean"`

      - `"datetime"`

    - `field_name: string`

  - `embedding_model: optional "@cf/qwen/qwen3-embedding-0.6b" or "@cf/qwen/qwen3-vl-embedding-2b" or "@cf/baai/bge-m3" or 8 more`

    - `"@cf/qwen/qwen3-embedding-0.6b"`

    - `"@cf/qwen/qwen3-vl-embedding-2b"`

    - `"@cf/baai/bge-m3"`

    - `"@cf/baai/bge-large-en-v1.5"`

    - `"@cf/google/embeddinggemma-300m"`

    - `"google-ai-studio/gemini-embedding-001"`

    - `"google-ai-studio/gemini-embedding-2-preview"`

    - `"google-ai-studio/gemini-embedding-2"`

    - `"openai/text-embedding-3-small"`

    - `"openai/text-embedding-3-large"`

    - `""`

  - `enable: optional boolean`

  - `engine_version: optional number`

  - `fusion_method: optional "max" or "rrf"`

    - `"max"`

    - `"rrf"`

  - `hybrid_search_enabled: optional boolean`

    Deprecated — use index_method instead.

  - `index_method: optional object { keyword, vector }`

    Controls which storage backends are used during indexing. Defaults to vector-only.

    - `keyword: boolean`

      Enable keyword (BM25) storage backend.

    - `vector: boolean`

      Enable vector (embedding) storage backend.

  - `indexing_options: optional object { keyword_tokenizer }`

    - `keyword_tokenizer: optional "porter" or "trigram"`

      Tokenizer used for keyword search indexing. porter provides word-level tokenization with Porter stemming (good for natural language queries). trigram enables character-level substring matching (good for partial matches, code, identifiers). Changing this triggers a full re-index. Defaults to porter.

      - `"porter"`

      - `"trigram"`

  - `last_activity: optional string`

  - `max_num_results: optional number`

  - `metadata: optional object { created_from_aisearch_wizard, worker_domain }`

    - `created_from_aisearch_wizard: optional boolean`

    - `worker_domain: optional string`

  - `modified_by: optional string`

  - `namespace: optional string`

  - `paused: optional boolean`

  - `public_endpoint_id: optional string`

  - `public_endpoint_params: optional object { authorized_hosts, chat_completions_endpoint, custom_domains, 4 more }`

    - `authorized_hosts: optional array of string`

    - `chat_completions_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable chat completions endpoint for this public endpoint

    - `custom_domains: optional array of string`

      Custom domain hostnames that alias this public endpoint. GET and create responses return the current set; on update (PUT) this field is only echoed back when supplied in the request body, otherwise it is null (omit it to leave domains unchanged).

    - `enabled: optional boolean`

    - `mcp: optional object { description, disabled }`

      - `description: optional string`

      - `disabled: optional boolean`

        Disable MCP endpoint for this public endpoint

    - `rate_limit: optional object { period_ms, requests, technique }`

      - `period_ms: optional number`

      - `requests: optional number`

      - `technique: optional "fixed" or "sliding"`

        - `"fixed"`

        - `"sliding"`

    - `search_endpoint: optional object { disabled }`

      - `disabled: optional boolean`

        Disable search endpoint for this public endpoint

  - `reranking: optional boolean`

  - `reranking_model: optional "@cf/baai/bge-reranker-base" or ""`

    - `"@cf/baai/bge-reranker-base"`

    - `""`

  - `retrieval_options: optional object { boost_by, keyword_match_mode }`

    - `boost_by: optional array of object { field, direction }`

      Metadata fields to boost search results by. Each entry specifies a metadata field and an optional direction. Direction defaults to 'asc' for numeric/datetime fields and 'exists' for text/boolean fields. Fields must match 'timestamp' or a defined custom_metadata field.

      - `field: string`

        Metadata field name to boost by. Use 'timestamp' for document freshness, or any custom_metadata field. Numeric and datetime fields support all four directions (asc, desc, exists, not_exists); text/boolean fields only support exists/not_exists.

      - `direction: optional "asc" or "desc" or "exists" or "not_exists"`

        Boost direction. 'desc' = higher values rank higher (e.g. newer timestamps). 'asc' = lower values rank higher. 'exists' = boost chunks that have the field. 'not_exists' = boost chunks that lack the field. Optional — defaults to 'asc' for numeric/datetime fields, 'exists' for text/boolean fields.

        - `"asc"`

        - `"desc"`

        - `"exists"`

        - `"not_exists"`

    - `keyword_match_mode: optional "and" or "or"`

      Controls which documents are candidates for BM25 scoring. 'and' restricts candidates to documents containing all query terms; 'or' includes any document containing at least one term, ranked by BM25 relevance. When omitted on an update, the existing stored value is preserved; when never set, search falls back to 'and'.

      - `"and"`

      - `"or"`

  - `rewrite_model: optional "@cf/meta/llama-3.3-70b-instruct-fp8-fast" or "@cf/zai-org/glm-4.7-flash" or "@cf/meta/llama-3.1-8b-instruct-fast" or 27 more`

    - `"@cf/meta/llama-3.3-70b-instruct-fp8-fast"`

    - `"@cf/zai-org/glm-4.7-flash"`

    - `"@cf/meta/llama-3.1-8b-instruct-fast"`

    - `"@cf/meta/llama-3.1-8b-instruct-fp8"`

    - `"@cf/meta/llama-4-scout-17b-16e-instruct"`

    - `"@cf/qwen/qwen3-30b-a3b-fp8"`

    - `"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b"`

    - `"@cf/moonshotai/kimi-k2-instruct"`

    - `"@cf/google/gemma-3-12b-it"`

    - `"@cf/google/gemma-4-26b-a4b-it"`

    - `"@cf/moonshotai/kimi-k2.5"`

    - `"anthropic/claude-3-7-sonnet"`

    - `"anthropic/claude-sonnet-4"`

    - `"anthropic/claude-opus-4"`

    - `"anthropic/claude-3-5-haiku"`

    - `"cerebras/qwen-3-235b-a22b-instruct"`

    - `"cerebras/qwen-3-235b-a22b-thinking"`

    - `"cerebras/llama-3.3-70b"`

    - `"cerebras/llama-4-maverick-17b-128e-instruct"`

    - `"cerebras/llama-4-scout-17b-16e-instruct"`

    - `"cerebras/gpt-oss-120b"`

    - `"google-ai-studio/gemini-2.5-flash"`

    - `"google-ai-studio/gemini-2.5-pro"`

    - `"grok/grok-4"`

    - `"groq/llama-3.3-70b-versatile"`

    - `"groq/llama-3.1-8b-instant"`

    - `"openai/gpt-5"`

    - `"openai/gpt-5-mini"`

    - `"openai/gpt-5-nano"`

    - `""`

  - `rewrite_query: optional boolean`

  - `score_threshold: optional number`

  - `source: optional string`

  - `source_params: optional object { exclude_items, include_items, prefix, 2 more }`

    - `exclude_items: optional array of string`

      List of path patterns to exclude. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /admin/** matches /admin/users and /admin/settings/advanced)

    - `include_items: optional array of string`

      List of path patterns to include. Uses micromatch glob syntax: * matches within a path segment, ** matches across path segments (e.g., /blog/** matches /blog/post and /blog/2024/post)

    - `prefix: optional string`

    - `r2_jurisdiction: optional string`

    - `web_crawler: optional object { parse_options, parse_type }`

      - `parse_options: optional object { content_selector, include_headers, include_images, 2 more }`

        - `content_selector: optional array of object { path, selector }`

          List of path-to-selector mappings for extracting specific content from crawled pages. Each entry pairs a URL glob pattern with a CSS selector. The first matching path wins. Only the matched HTML fragment is stored and indexed. Omit the field to disable content selection — empty arrays are rejected.

          - `path: string`

            Glob pattern to match against the page URL path. Uses standard glob syntax: * matches within a segment, ** crosses directories.

          - `selector: string`

            CSS selector to extract content from pages matching the path pattern. Must not contain disallowed characters (;, `, $, {, }, ). Must target a single element; if multiple elements match, the selector is ignored and the full page is used.

        - `include_headers: optional map[string]`

          Up to 5 custom HTTP headers sent with each crawl request. Names must be RFC-7230 token characters (no spaces, colons, or control characters); values must be HTAB + printable ASCII (no CR/LF).

        - `include_images: optional boolean`

        - `specific_sitemaps: optional array of string`

          List of specific sitemap URLs to use for crawling. Only valid when parse_type is 'sitemap'.

        - `use_browser_rendering: optional boolean`

      - `parse_type: optional "sitemap" or "crawl"`

        - `"sitemap"`

        - `"crawl"`

  - `status: optional string`

  - `sync_interval: optional 900 or 1800 or 3600 or 5 more`

    Interval between automatic syncs, in seconds. Allowed values: 900 (15min), 1800 (30min), 3600 (1h), 7200 (2h), 14400 (4h), 21600 (6h), 43200 (12h), 86400 (24h).

    - `900`

    - `1800`

    - `3600`

    - `7200`

    - `14400`

    - `21600`

    - `43200`

    - `86400`

  - `token_id: optional string`

  - `type: optional "r2" or "web-crawler"`

    - `"r2"`

    - `"web-crawler"`

### Instance Stats Response

- `InstanceStatsResponse object { completed, degraded, engine, 8 more }`

  - `completed: optional number`

  - `degraded: optional boolean`

    True when status counts are unavailable (e.g. legacy stats query exceeded D1 statement-size limit). Counts are omitted in this case.

  - `engine: optional object { r2, vectorize }`

    Engine-specific metadata. Present only for managed (v3) instances.

    - `r2: optional object { metadataSizeBytes, objectCount, payloadSizeBytes }`

      R2 bucket storage usage in bytes.

      - `metadataSizeBytes: number`

      - `objectCount: number`

      - `payloadSizeBytes: number`

    - `vectorize: optional object { dimensions, vectorsCount }`

      Vectorize index metadata (dimensions, vector count).

      - `dimensions: number`

      - `vectorsCount: number`

  - `error: optional number`

  - `file_embed_errors: optional map[unknown]`

  - `index_source_errors: optional map[unknown]`

  - `last_activity: optional string`

  - `outdated: optional number`

  - `queued: optional number`

  - `running: optional number`

  - `skipped: optional number`

### Instance Search Response

- `InstanceSearchResponse object { chunks, query_kind, search_query }`

  - `chunks: array of object { id, score, text, 3 more }`

    - `id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `query_kind: "text" or "image" or "multimodal"`

    - `"text"`

    - `"image"`

    - `"multimodal"`

  - `search_query: optional string`

### Instance Chat Completions Response

- `InstanceChatCompletionsResponse object { choices, chunks, id, 2 more }`

  - `choices: array of object { message, index }`

    - `message: object { content, role }`

      - `content: string or array of object { text, type }  or object { image_url, type }`

        - `string`

        - `array of object { text, type }  or object { image_url, type }`

          - `object { text, type }`

            - `text: string`

            - `type: "text"`

              - `"text"`

          - `object { image_url, type }`

            - `image_url: object { url }`

              - `url: string`

            - `type: "image_url"`

              - `"image_url"`

      - `role: "system" or "developer" or "user" or 2 more`

        - `"system"`

        - `"developer"`

        - `"user"`

        - `"assistant"`

        - `"tool"`

    - `index: optional number`

  - `chunks: array of object { id, score, text, 3 more }`

    - `id: string`

    - `score: number`

    - `text: string`

    - `type: string`

    - `item: optional object { key, metadata, timestamp }`

      - `key: string`

      - `metadata: optional map[unknown]`

      - `timestamp: optional number`

    - `scoring_details: optional object { fusion_method, keyword_rank, keyword_score, 3 more }`

      - `fusion_method: optional "rrf" or "max"`

        - `"rrf"`

        - `"max"`

      - `keyword_rank: optional number`

      - `keyword_score: optional number`

      - `reranking_score: optional number`

      - `vector_rank: optional number`

      - `vector_score: optional number`

  - `id: optional string`

  - `model: optional string`

  - `object: optional string`

# Jobs

## List Jobs

**get** `/accounts/{account_id}/ai-search/instances/{id}/jobs`

Lists indexing jobs for an AI Search instance.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Query Parameters

- `page: optional number`

- `per_page: optional number`

### Returns

- `result: array of object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/jobs \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "id",
      "source": "user",
      "description": "description",
      "end_reason": "end_reason",
      "ended_at": "ended_at",
      "last_seen_at": "last_seen_at",
      "started_at": "started_at"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Create new job

**post** `/accounts/{account_id}/ai-search/instances/{id}/jobs`

Creates a new indexing job for an AI Search instance.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

### Body Parameters

- `description: optional string`

### Returns

- `result: object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/jobs \
    -X POST \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "id",
    "source": "user",
    "description": "description",
    "end_reason": "end_reason",
    "ended_at": "ended_at",
    "last_seen_at": "last_seen_at",
    "started_at": "started_at"
  },
  "success": true
}
```

## Get a Job Details

**get** `/accounts/{account_id}/ai-search/instances/{id}/jobs/{job_id}`

Retrieves details for a specific AI Search indexing job.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `job_id: string`

### Returns

- `result: object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/jobs/$JOB_ID \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "id",
    "source": "user",
    "description": "description",
    "end_reason": "end_reason",
    "ended_at": "ended_at",
    "last_seen_at": "last_seen_at",
    "started_at": "started_at"
  },
  "success": true
}
```

## List Job Logs

**get** `/accounts/{account_id}/ai-search/instances/{id}/jobs/{job_id}/logs`

Lists log entries for an AI Search indexing job.

### Path Parameters

- `account_id: string`

- `id: string`

  AI Search instance ID. Lowercase alphanumeric, hyphens, and underscores.

- `job_id: string`

### Query Parameters

- `page: optional number`

- `per_page: optional number`

### Returns

- `result: array of object { id, created_at, message, message_type }`

  - `id: number`

  - `created_at: number`

  - `message: string`

  - `message_type: number`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: boolean`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/instances/$ID/jobs/$JOB_ID/logs \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": 0,
      "created_at": 0,
      "message": "message",
      "message_type": 0
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Domain Types

### Job List Response

- `JobListResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Create Response

- `JobCreateResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Get Response

- `JobGetResponse object { id, source, description, 4 more }`

  - `id: string`

  - `source: "user" or "schedule"`

    - `"user"`

    - `"schedule"`

  - `description: optional string`

  - `end_reason: optional string`

  - `ended_at: optional string`

  - `last_seen_at: optional string`

  - `started_at: optional string`

### Job Logs Response

- `JobLogsResponse = array of object { id, created_at, message, message_type }`

  - `id: number`

  - `created_at: number`

  - `message: string`

  - `message_type: number`

# Tokens

## List tokens.

**get** `/accounts/{account_id}/ai-search/tokens`

List tokens.

### Path Parameters

- `account_id: string`

### Query Parameters

- `page: optional number`

  Page number (1-indexed).

- `per_page: optional number`

  Number of results per page.

- `search: optional string`

  Filter tokens whose name contains this string (case-insensitive).

### Returns

- `result: array of object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

- `result_info: object { count, page, per_page, total_count }`

  - `count: number`

  - `page: number`

  - `per_page: number`

  - `total_count: number`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/tokens \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": [
    {
      "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
      "cf_api_id": "cf_api_id",
      "created_at": "2019-12-27T18:11:19.117Z",
      "modified_at": "2019-12-27T18:11:19.117Z",
      "name": "name",
      "created_by": "created_by",
      "enabled": true,
      "legacy": true,
      "modified_by": "modified_by"
    }
  ],
  "result_info": {
    "count": 0,
    "page": 0,
    "per_page": 0,
    "total_count": 0
  },
  "success": true
}
```

## Create token.

**post** `/accounts/{account_id}/ai-search/tokens`

Create a new token.

### Path Parameters

- `account_id: string`

### Body Parameters

- `cf_api_id: string`

- `cf_api_key: string`

- `name: string`

- `legacy: optional boolean`

### Returns

- `result: object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/tokens \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "cf_api_id": "a1b2c3d4e5f6",
          "cf_api_key": "abc123",
          "name": "my-token"
        }'
```

#### Response

```json
{
  "result": {
    "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "cf_api_id": "cf_api_id",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "name": "name",
    "created_by": "created_by",
    "enabled": true,
    "legacy": true,
    "modified_by": "modified_by"
  },
  "success": true
}
```

## Read token.

**get** `/accounts/{account_id}/ai-search/tokens/{id}`

Read token.

### Path Parameters

- `account_id: string`

- `id: string`

### Returns

- `result: object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/tokens/$ID \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {
    "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "cf_api_id": "cf_api_id",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "name": "name",
    "created_by": "created_by",
    "enabled": true,
    "legacy": true,
    "modified_by": "modified_by"
  },
  "success": true
}
```

## Update token.

**put** `/accounts/{account_id}/ai-search/tokens/{id}`

Update token.

### Path Parameters

- `account_id: string`

- `id: string`

### Body Parameters

- `cf_api_id: string`

- `cf_api_key: string`

- `name: string`

- `legacy: optional boolean`

### Returns

- `result: object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/tokens/$ID \
    -X PUT \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -d '{
          "cf_api_id": "a1b2c3d4e5f6",
          "cf_api_key": "abc123",
          "name": "my-token"
        }'
```

#### Response

```json
{
  "result": {
    "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
    "cf_api_id": "cf_api_id",
    "created_at": "2019-12-27T18:11:19.117Z",
    "modified_at": "2019-12-27T18:11:19.117Z",
    "name": "name",
    "created_by": "created_by",
    "enabled": true,
    "legacy": true,
    "modified_by": "modified_by"
  },
  "success": true
}
```

## Delete token.

**delete** `/accounts/{account_id}/ai-search/tokens/{id}`

Delete token.

### Path Parameters

- `account_id: string`

- `id: string`

### Returns

- `result: unknown`

- `success: true`

  - `true`

### Example

```http
curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-search/tokens/$ID \
    -X DELETE \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

#### Response

```json
{
  "result": {},
  "success": true
}
```

## Domain Types

### Token List Response

- `TokenListResponse object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

### Token Create Response

- `TokenCreateResponse object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

### Token Read Response

- `TokenReadResponse object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

### Token Update Response

- `TokenUpdateResponse object { id, cf_api_id, created_at, 6 more }`

  - `id: string`

  - `cf_api_id: string`

  - `created_at: string`

  - `modified_at: string`

  - `name: string`

  - `created_by: optional string`

  - `enabled: optional boolean`

  - `legacy: optional boolean`

  - `modified_by: optional string`

### Token Delete Response

- `TokenDeleteResponse = unknown`

---

---
title: REST API
description: Manage AI Search instances and sync jobs over HTTP using the Instances REST API.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# REST API

Use the AI Search REST API to manage instances and sync jobs over HTTP.

## Authentication

All requests require an API token with **AI Search:Edit** and **AI Search:Run** permissions.

1. In the Cloudflare dashboard, go to **My Profile** \> **API Tokens**.  
[ Go to **API Tokens** ](https://dash.cloudflare.com/profile/api-tokens)
2. Select **Create Token**.
3. Select **Create Custom Token**.
4. Enter a **Token name**, for example `AI Search Manager`.
5. Under **Permissions**, add two permissions:

  * **Account** \> **AI Search:Edit**
  * **Account** \> **AI Search:Run**
6. Select **Continue to summary**, then select **Create Token**.
7. Copy and save the token value. This is your `API_TOKEN`.

Include the token in the `Authorization` header for all requests:

```txt
Authorization: Bearer <API_TOKEN>
```

## API paths

AI Search APIs are available at two base paths:

| Path                                                                    | Description                                                                                                  |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| /accounts/{account\_id}/ai-search/instances/{id}                        | Operates on a specific instance                                                                              |
| /accounts/{account\_id}/ai-search/namespaces/{namespace}/instances/{id} | Operates on instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) |

The available operations are the same for both paths. For the namespace-scoped API, refer to the [Namespace API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/).

## Instances

Create, list, get, update, and delete AI Search instances. For the full specification, refer to the [Instances API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/).

| Operation                                                                                                    | Method | Description                   |
| ------------------------------------------------------------------------------------------------------------ | ------ | ----------------------------- |
| [Create](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/create/) | POST   | Create a new instance         |
| [List](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/list/)     | GET    | List all instances            |
| [Get](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/read/)      | GET    | Get an instance by ID         |
| [Update](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/update/) | PUT    | Update instance configuration |
| [Delete](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/delete/) | DELETE | Delete an instance            |
| [Stats](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/stats/)   | GET    | Get indexing statistics       |

### Example: Create an instance

Create an instance in the default namespace:

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-instance"
  }'
```

## Jobs

Trigger and monitor [sync jobs](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) that scan your data source and index new or updated content. For the full specification, refer to the [Jobs API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/subresources/jobs/).

| Operation                                                                                                                      | Method | Description                   |
| ------------------------------------------------------------------------------------------------------------------------------ | ------ | ----------------------------- |
| [Create](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/subresources/jobs/methods/create/) | POST   | Trigger a new sync job        |
| [List](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/subresources/jobs/methods/list/)     | GET    | List all jobs for an instance |
| [Get](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/subresources/jobs/methods/read/)      | GET    | Get job details               |
| [Logs](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/subresources/jobs/methods/logs/)     | GET    | View job logs                 |

### Example: Trigger a sync job

Start a new sync job for an instance:

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/jobs" \
  -H "Authorization: Bearer <API_TOKEN>"
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/instances/rest-api/#page","headline":"REST API · Cloudflare AI Search docs","description":"Manage AI Search instances and sync jobs over HTTP using the Instances REST API.","url":"https://developers.cloudflare.com/ai-search/api/instances/rest-api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/instances/","name":"Instances"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/instances/rest-api/","name":"REST API"}}]}
```

---

---
title: Workers binding
description: Manage AI Search instances from a Cloudflare Worker using the Instances Workers binding.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Workers binding

[Workers](https://developers.cloudflare.com/workers/) provides a serverless execution environment that allows you to create new applications or augment existing ones. Use a [Workers binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/) to create, list, update, and delete AI Search instances from a Cloudflare Worker. You can also check instance configuration and monitor indexing progress.

## Configure the binding

To use AI Search with Workers, you must create an AI Search binding. You create bindings by updating your [Wrangler configuration](https://developers.cloudflare.com/workers/wrangler/configuration/). AI Search provides two types of bindings:

* Namespace binding: `ai_search_namespaces`
* Instance binding: `ai_search`

### Namespace binding

Access all instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/). You can get, create, list, and delete instances at runtime.

* [  wrangler.jsonc ](#tab-panel-7176)
* [  wrangler.toml ](#tab-panel-7177)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "my-namespace"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "my-namespace"
```

| Field     | Type    | Required | Description                                                                                                                                                                                                                   |
| --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| binding   | string  | Yes      | The variable name available on env. For example, "AI\_SEARCH" makes it accessible as env.AI\_SEARCH.                                                                                                                          |
| namespace | string  | Yes      | The [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) to bind to. A default namespace is created automatically for every account. If the namespace does not exist, Wrangler creates it on deploy. |
| remote    | boolean | No       | Set to true for local development with wrangler dev.                                                                                                                                                                          |

### Instance binding

Bind directly to a single instance in the `default` namespace. Use this when you know which instance you need at deploy time.

* [  wrangler.jsonc ](#tab-panel-7178)
* [  wrangler.toml ](#tab-panel-7179)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search": [
    {
      "binding": "MY_SEARCH",
      "instance_name": "my-instance"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search]]
binding = "MY_SEARCH"
instance_name = "my-instance"
```

| Field          | Type    | Required | Description                                                                                          |
| -------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------- |
| binding        | string  | Yes      | The variable name available on env. For example, "MY\_SEARCH" makes it accessible as env.MY\_SEARCH. |
| instance\_name | string  | Yes      | The name of the AI Search instance. Must exist in the default namespace at deploy time.              |
| remote         | boolean | No       | Set to true for local development with wrangler dev.                                                 |

## Namespace methods

The following methods are only available when using the `ai_search_namespaces` binding. The namespace handle (`env.AI_SEARCH`) exposes methods for working with instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/).

### `get()`

Returns a handle to a specific instance. This is **synchronous** and does not make a network call. The instance is resolved lazily when you call methods like `search()` or `info()`.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");
const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
});
```

#### Parameters

| Parameter | Type   | Required | Description                                  |
| --------- | ------ | -------- | -------------------------------------------- |
| name      | string | Yes      | The name of the instance to get a handle to. |

### `list()`

Returns all instances within the namespace.

**TypeScript**

```ts
const { result, result_info } = await env.AI_SEARCH.list();


for (const instance of result) {
  console.log(`${instance.id} (${instance.type}) - ${instance.status}`);
}
// result_info.total_count contains the total number of instances
```

#### Parameters

| Parameter            | Type   | Required | Description                                                     |
| -------------------- | ------ | -------- | --------------------------------------------------------------- |
| page                 | number | No       | The page number to return. Defaults to 1.                       |
| per\_page            | number | No       | The number of instances per page. Defaults to 20. Maximum 100.  |
| search               | string | No       | Search instances by ID.                                         |
| order\_by            | string | No       | Sort column. Valid value: created\_at. Defaults to created\_at. |
| order\_by\_direction | string | No       | Sort direction. Valid values: asc, desc. Defaults to desc.      |

#### Response

| Field                     | Type    | Description                                                          |
| ------------------------- | ------- | -------------------------------------------------------------------- |
| result                    | array   | Array of instance objects.                                           |
| result\[\].id             | string  | The instance identifier.                                             |
| result\[\].type           | string  | The data source type (r2, web-crawler, or null for empty instances). |
| result\[\].source         | string  | The data source location.                                            |
| result\[\].status         | string  | The instance status (active, waiting, indexing).                     |
| result\[\].enable         | boolean | Whether the instance is enabled.                                     |
| result\[\].namespace      | string  | The namespace the instance belongs to.                               |
| result\[\].created\_at    | string  | ISO 8601 timestamp of when the instance was created.                 |
| result\[\].modified\_at   | string  | ISO 8601 timestamp of the last modification.                         |
| result\_info              | object  | Pagination metadata.                                                 |
| result\_info.total\_count | number  | Total number of instances in the namespace.                          |

### `create()`

Creates a new instance and returns a handle to it. You can create instances backed by a data source or create empty instances for use with the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/).

**Create an empty instance for file uploads:**

AI Search instances come with [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) where you can upload documents directly.

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "knowledge-base",
});


// Upload documents using the Items API
await instance.items.upload("guide.pdf", pdfArrayBuffer);
```

**Create a web-crawler instance:**

Automatically crawl and index a website that you own. For more configuration options, refer to [Website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/).

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "my-docs",
  type: "web-crawler",
  source: "developers.cloudflare.com",
});
```

**Create an R2-backed instance:**

Index documents stored in an [R2](https://developers.cloudflare.com/r2/) bucket. For more configuration options, refer to [R2 data source](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/).

**TypeScript**

```ts
const instance = await env.AI_SEARCH.create({
  id: "internal-docs",
  type: "r2",
  source: "my-docs-bucket",
});
```

#### Parameters

`id` ` string ` required

The unique identifier for the AI Search instance. Must be 1-64 characters and match the pattern `^[a-z0-9_]+(?:-[a-z0-9_]+)*$`.

---

`type` ` string ` optional

The type of data source. Valid values: `r2`, `web-crawler`. Required when creating an instance with a data source. Omit when creating an empty instance for use with the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/).

---

`source` ` string ` optional

The data source location. For `r2` type, this is the R2 bucket name. For `web-crawler` type, this is the website domain. Required when `type` is specified.

---

`source_params` ` object ` optional

Additional parameters for the data source.

* `prefix` ` string ` optional

  * For R2 sources, limits indexing to objects with this key prefix.
* `r2_jurisdiction` ` string ` optional

  * The jurisdiction for the R2 bucket, for example `eu`.
* `include_items` ` array ` optional

  * Glob patterns for paths to include in indexing. For example: `["/blog/**", "/docs/**/*.html"]`.
* `exclude_items` ` array ` optional

  * Glob patterns for paths to exclude from indexing. For example: `["/admin/**", "/private/**"]`.
* `web_crawler` ` object ` optional

  * Configuration for web crawler sources.
  * `parse_type` ` string ` optional

    * The parsing method. Valid value: `sitemap`.
  * `parse_options` ` object ` optional

    * `include_headers` ` object ` optional

      * Custom HTTP headers to include when crawling.
    * `include_images` ` boolean ` optional

      * Whether to include images in the index.
    * `specific_sitemaps` ` array ` optional

      * Specific sitemap URLs to crawl. For example: `["https://example.com/sitemap.xml"]`.
    * `use_browser_rendering` ` boolean ` optional

      * Use Browser Run (formerly Browser Rendering) to crawl JavaScript-rendered pages.
  * `store_options` ` object ` optional

    * `storage_type` ` string ` optional

      * The storage type. Valid value: `r2`.
    * `storage_id` ` string ` optional

      * The storage bucket ID.
    * `r2_jurisdiction` ` string ` optional

      * The jurisdiction for the storage bucket.

---

`index_method` ` object ` optional

Configures which indexing methods are enabled for the instance. Determines whether vector (semantic) search, keyword search, or both are available. At least one must be `true`.

* `vector` ` boolean ` optional

  * Enable vector-based semantic search. Defaults to `true`.
* `keyword` ` boolean ` optional

  * Enable keyword-based search. Defaults to `false`.

Set both to `true` for hybrid search.

---

`fusion_method` ` string ` optional

Controls how vector and keyword scores are combined when using hybrid search. Valid values: `rrf` (Reciprocal Rank Fusion), `max` (takes the maximum score). Defaults to `rrf`.

---

`indexing_options` ` object ` optional

Configuration for how content is indexed.

* `keyword_tokenizer` ` string ` optional  
  * The tokenizer used for keyword search indexing. Valid values: `porter` (stemming-based), `trigram` (character n-gram). Defaults to `porter`.

---

`retrieval_options` ` object ` optional

Default retrieval configuration for the instance. These defaults can be overridden per-request using `ai_search_options`.

* `keyword_match_mode` ` string ` optional

  * Controls how keyword (BM25) matching selects candidate documents. `and` requires all terms to match. `or` requires any term to match. Defaults to `and`.
* `boost_by` ` array ` optional

  * Default boost fields applied to all search queries. Maximum 3 items. Each item has:  
    * `field` ` string ` required \- The metadata field name to boost by. Maximum 64 characters.
    * `direction` ` string ` optional \- The boost direction. Valid values: `asc`, `desc`, `exists`, `not_exists`.

---

`sync_interval` ` number ` optional

Seconds between automatic data source syncs. Valid values: `3600`, `7200`, `14400`, `21600`, `43200`, `86400`. Defaults to `21600` (6 hours).

---

`token_id` ` string ` optional

The UUID of the [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) to use for this instance. Only required if you have never created an AI Search instance before. Refer to the [API get started guide](https://developers.cloudflare.com/ai-search/get-started/api/) for how to create and register a service token.

---

`ai_gateway_id` ` string ` optional

The AI Gateway ID to route requests through for logging and analytics.

---

`embedding_model` ` string ` optional

The embedding model to use for vectorizing content.

---

`ai_search_model` ` string ` optional

The text-generation model to use for generating responses.

---

`rewrite_query` ` boolean ` optional

Enable query rewriting to improve retrieval accuracy. Defaults to `false`.

---

`rewrite_model` ` string ` optional

The model to use for query rewriting.

---

`reranking` ` boolean ` optional

Enable reranking to reorder retrieved results by semantic relevance. Defaults to `false`.

---

`reranking_model` ` string ` optional

The reranking model to use. Valid value: `@cf/baai/bge-reranker-base`.

---

`chunk_size` ` number ` optional

The size of chunks when splitting documents. Minimum value: `64`.

---

`chunk_overlap` ` number ` optional

The overlap between chunks. Minimum value: `0`.

---

`max_num_results` ` number ` optional

The default maximum number of results to return. Minimum value: `1`.

---

`score_threshold` ` number ` optional

The default minimum score threshold for results. Minimum value: `0`.

---

`cache` ` boolean ` optional

Enable response caching. Defaults to `true`.

---

`cache_threshold` ` string ` optional

The cache matching threshold. Valid values: `super_strict_match`, `close_enough`, `flexible_friend`, `anything_goes`. Defaults to `close_enough`.

---

`cache_ttl` ` number ` optional

The cache entry TTL in seconds. Valid values are `600`, `1800`, `3600`, `7200`, `21600`, `43200`, `86400`, `172800`, `259200`, and `518400`. Defaults to `172800`.

---

`custom_metadata` ` array ` optional

Custom metadata fields to extract and index from documents.

* `field_name` ` string ` required

  * The name of the metadata field.
* `data_type` ` string ` required

  * The data type of the field. Valid values: `text`, `number`, `boolean`, `datetime`.

---

`enable` ` boolean ` optional

Whether the instance is enabled. Defaults to `true`.

#### Response

Returns an `AiSearchInstance` handle that is immediately usable for calling methods like `search()`, `info()`, `stats()`, and `items.upload()`. Call `info()` on the handle to get the instance configuration.

### `delete()`

Permanently deletes an instance and all its indexed content. This action cannot be undone.

**TypeScript**

```ts
await env.AI_SEARCH.delete("old-docs");
```

#### Parameters

| Parameter | Type   | Required | Description                         |
| --------- | ------ | -------- | ----------------------------------- |
| name      | string | Yes      | The name of the instance to delete. |

#### Response

Returns `void`. Throws an error if the instance does not exist.

Warning

Deleting an instance permanently removes all indexed data, including embeddings, chunks, and source files.

## Instance methods

The following methods are available on both the `ai_search_namespaces` and `ai_search` bindings. With the namespace binding, call methods on the handle returned by `get()`. With the instance binding, call methods directly on the binding (for example, `env.MY_SEARCH.info()`).

The examples below use the namespace binding.

### `update()`

Partially updates the instance configuration. Only the fields you pass are modified.

**TypeScript**

```ts
const updated = await env.AI_SEARCH.get("my-instance").update({
  ai_search_model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
  reranking: true,
});
```

#### Parameters

Accepts a partial version of the [create parameters](#parameters). Only the fields you include are updated.

| Field              | Type    | Description                                                           |
| ------------------ | ------- | --------------------------------------------------------------------- |
| ai\_search\_model  | string  | The text-generation model.                                            |
| embedding\_model   | string  | The embedding model.                                                  |
| index\_method      | object  | Indexing methods: \\{ vector: boolean, keyword: boolean \\}.          |
| fusion\_method     | string  | How vector and keyword scores are combined (rrf or max).              |
| indexing\_options  | object  | Indexing configuration including keyword\_tokenizer.                  |
| retrieval\_options | object  | Retrieval configuration including keyword\_match\_mode and boost\_by. |
| reranking          | boolean | Turn on or off reranking.                                             |
| reranking\_model   | string  | The reranking model.                                                  |
| rewrite\_query     | boolean | Turn on or off query rewriting.                                       |
| rewrite\_model     | string  | The query rewriting model.                                            |
| source             | string  | Update the data source location.                                      |
| cache              | boolean | Turn on or off response caching.                                      |
| chunk\_size        | number  | Token size of each chunk.                                             |
| chunk\_overlap     | number  | Token overlap between chunks.                                         |
| score\_threshold   | number  | Minimum score threshold for results.                                  |
| max\_num\_results  | number  | Maximum number of results per query.                                  |
| custom\_metadata   | array   | Custom metadata field definitions.                                    |
| sync\_interval     | number  | Seconds between automatic data source syncs.                          |

#### Response

Returns the updated instance configuration. Same shape as [info()](#response-2).

### `info()`

Returns the current configuration and metadata for the instance.

**TypeScript**

```ts
const info = await env.AI_SEARCH.get("my-instance").info();
```

#### Response

| Field              | Type    | Description                                                           |
| ------------------ | ------- | --------------------------------------------------------------------- |
| id                 | string  | The instance identifier.                                              |
| type               | string  | The data source type (r2, web-crawler, or null).                      |
| source             | string  | The data source location.                                             |
| namespace          | string  | The namespace the instance belongs to.                                |
| status             | string  | The instance status (active, waiting, indexing).                      |
| enable             | boolean | Whether the instance is enabled.                                      |
| created\_at        | string  | Timestamp of when the instance was created.                           |
| modified\_at       | string  | Timestamp of the last modification.                                   |
| ai\_search\_model  | string  | The text-generation model.                                            |
| embedding\_model   | string  | The embedding model.                                                  |
| reranking          | boolean | Whether reranking is enabled.                                         |
| reranking\_model   | string  | The reranking model.                                                  |
| rewrite\_query     | boolean | Whether query rewriting is enabled.                                   |
| rewrite\_model     | string  | The query rewriting model.                                            |
| cache              | boolean | Whether response caching is enabled.                                  |
| cache\_threshold   | string  | The similarity threshold for cache hits.                              |
| index\_method      | object  | Which indexing methods are enabled (vector, keyword).                 |
| fusion\_method     | string  | How vector and keyword scores are combined (rrf or max).              |
| indexing\_options  | object  | Indexing configuration including keyword\_tokenizer.                  |
| retrieval\_options | object  | Retrieval configuration including keyword\_match\_mode and boost\_by. |
| chunk\_size        | number  | Token size of each chunk.                                             |
| chunk\_overlap     | number  | Token overlap between chunks.                                         |
| score\_threshold   | number  | Minimum score threshold for results.                                  |
| max\_num\_results  | number  | Maximum number of results per query.                                  |
| sync\_interval     | number  | Seconds between automatic data source syncs.                          |
| custom\_metadata   | array   | Custom metadata field definitions.                                    |
| last\_activity     | string  | Timestamp of the last indexing activity.                              |

### `stats()`

Returns the current indexing progress for the instance. Use this to poll for completion after creating an instance or uploading files.

**TypeScript**

```ts
const stats = await env.AI_SEARCH.get("my-instance").stats();
```

#### Response

| Field                         | Type   | Description                                       |
| ----------------------------- | ------ | ------------------------------------------------- |
| queued                        | number | Items waiting to be processed.                    |
| running                       | number | Items currently being processed.                  |
| completed                     | number | Items successfully indexed.                       |
| error                         | number | Items that failed to index.                       |
| skipped                       | number | Items skipped during indexing.                    |
| outdated                      | number | Items that need re-indexing.                      |
| last\_activity                | string | ISO 8601 timestamp of the last indexing activity. |
| file\_embed\_errors           | object | Map of file IDs to embedding error details.       |
| engine.vectorize.vectorsCount | number | Total number of vectors stored.                   |
| engine.vectorize.dimensions   | number | Dimensions of the vector embeddings.              |
| engine.r2.payloadSizeBytes    | number | Total size of stored payloads in bytes.           |
| engine.r2.metadataSizeBytes   | number | Total size of stored metadata in bytes.           |
| engine.r2.objectCount         | number | Total number of objects in storage.               |

## Local development

Local development is supported by proxying requests to your deployed AI Search instance. Add `remote: true` to your binding configuration to enable local development with `wrangler dev`.

**JSONC**

```jsonc
// wrangler.jsonc
{
  "ai_search": [
    {
      "binding": "MY_SEARCH",
      "instance_name": "my-instance",
      "remote": true,
    },
  ],
}
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/instances/workers-binding/#page","headline":"Workers binding · Cloudflare AI Search docs","description":"Manage AI Search instances from a Cloudflare Worker using the Instances Workers binding.","url":"https://developers.cloudflare.com/ai-search/api/instances/workers-binding/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/instances/","name":"Instances"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/instances/workers-binding/","name":"Workers binding"}}]}
```

---

---
title: REST API
description: Upload, list, and manage documents in AI Search instances using the Items REST API.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# REST API

Use the AI Search REST API to upload, list, and manage individual documents within an instance.

Note

The Items API uploads files to an instance's built-in storage. For more details, refer to [Built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/).

## Authentication

All requests require an API token with **AI Search:Edit** and **AI Search:Run** permissions.

1. In the Cloudflare dashboard, go to **My Profile** \> **API Tokens**.  
[ Go to **API Tokens** ](https://dash.cloudflare.com/profile/api-tokens)
2. Select **Create Token**.
3. Select **Create Custom Token**.
4. Enter a **Token name**, for example `AI Search Manager`.
5. Under **Permissions**, add two permissions:

  * **Account** \> **AI Search:Edit**
  * **Account** \> **AI Search:Run**
6. Select **Continue to summary**, then select **Create Token**.
7. Copy and save the token value. This is your `API_TOKEN`.

Include the token in the `Authorization` header for all requests:

```txt
Authorization: Bearer <API_TOKEN>
```

## API paths

AI Search APIs are available at two base paths:

| Path                                                                     | Description                                                                                                  |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| /accounts/{account\_id}/ai-search/instances/{id}/                        | Operates on a specific instance                                                                              |
| /accounts/{account\_id}/ai-search/namespaces/{namespace}/instances/{id}/ | Operates on instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) |

The available operations are the same for both paths. For the namespace-scoped API, refer to the [Namespace API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/).

## Items

Upload, list, get, delete, and download items within an instance. For the full specification, refer to the [Items API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/).

| Operation                                                                                                                                                   | Method | Description                    |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------ |
| [Upload](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/upload/)     | POST   | Upload a document for indexing |
| [List](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/list/)         | GET    | List all items in an instance  |
| [Get](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/get/)           | GET    | Get item info by ID            |
| [Delete](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/delete/)     | DELETE | Delete an item                 |
| [Download](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/download/) | GET    | Download the original file     |

### Example: Upload a document

Upload a file to an instance:

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/items" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -F "file=@/path/to/your/file.pdf"
```

### Example: List items

List all items in an instance:

```bash
curl "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/items" \
  -H "Authorization: Bearer <API_TOKEN>"
```

To find a single item by its exact object key, pass the `key` query parameter:

```bash
curl "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/items?key=docs/readme.md" \
  -H "Authorization: Bearer <API_TOKEN>"
```

Keys are unique per data source, so combine `key` with `source` (for example, `source=builtin`) to disambiguate when the same key exists across multiple sources.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/items/rest-api/#page","headline":"REST API · Cloudflare AI Search docs","description":"Upload, list, and manage documents in AI Search instances using the Items REST API.","url":"https://developers.cloudflare.com/ai-search/api/items/rest-api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/items/","name":"Items"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/items/rest-api/","name":"REST API"}}]}
```

---

---
title: Workers binding
description: Upload, list, and manage documents in AI Search instances using the Items Workers binding.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Workers binding

[Workers](https://developers.cloudflare.com/workers/) provides a serverless execution environment that allows you to create new applications or augment existing ones. Use a [Workers binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/) to upload, list, and manage documents in your AI Search instances from a Cloudflare Worker. Access the Items API through the `items` property on an instance handle.

Note

The Items API uploads files to an instance's built-in storage. For more details, refer to [Built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/).

## Configure the binding

To use AI Search with Workers, you must create an AI Search binding. You create bindings by updating your [Wrangler configuration](https://developers.cloudflare.com/workers/wrangler/configuration/). AI Search provides two types of bindings:

* Namespace binding: `ai_search_namespaces`
* Instance binding: `ai_search`

### Namespace binding

Access all instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/). You can get, create, list, and delete instances at runtime.

* [  wrangler.jsonc ](#tab-panel-7180)
* [  wrangler.toml ](#tab-panel-7181)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "my-namespace"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "my-namespace"
```

| Field     | Type    | Required | Description                                                                                                                                                                                                                   |
| --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| binding   | string  | Yes      | The variable name available on env. For example, "AI\_SEARCH" makes it accessible as env.AI\_SEARCH.                                                                                                                          |
| namespace | string  | Yes      | The [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) to bind to. A default namespace is created automatically for every account. If the namespace does not exist, Wrangler creates it on deploy. |
| remote    | boolean | No       | Set to true for local development with wrangler dev.                                                                                                                                                                          |

### Instance binding

Bind directly to a single instance in the `default` namespace. Use this when you know which instance you need at deploy time.

* [  wrangler.jsonc ](#tab-panel-7182)
* [  wrangler.toml ](#tab-panel-7183)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search": [
    {
      "binding": "MY_SEARCH",
      "instance_name": "my-instance"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search]]
binding = "MY_SEARCH"
instance_name = "my-instance"
```

| Field          | Type    | Required | Description                                                                                          |
| -------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------- |
| binding        | string  | Yes      | The variable name available on env. For example, "MY\_SEARCH" makes it accessible as env.MY\_SEARCH. |
| instance\_name | string  | Yes      | The name of the AI Search instance. Must exist in the default namespace at deploy time.              |
| remote         | boolean | No       | Set to true for local development with wrangler dev.                                                 |

## Methods

The Items API methods are available on both the `ai_search_namespaces` and `ai_search` bindings. With the namespace binding, call methods on the handle returned by `get()`. With the instance binding, call methods directly on the binding (for example, `env.MY_SEARCH.items.upload()`).

The examples below use the namespace binding.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");
```

### `items.upload()`

Uploads a document for indexing. Returns immediately. The document is queued for processing.

**TypeScript**

```ts
// Upload from a string
await instance.items.upload(
  "faq.md",
  "# FAQ\n\nQ: How do I reset my password?\nA: Go to Settings > Security...",
);


// Upload from an ArrayBuffer
const pdfResponse = await fetch("https://example.com/guide.pdf");
const pdfBuffer = await pdfResponse.arrayBuffer();
await instance.items.upload("guide.pdf", pdfBuffer);


// Upload from a ReadableStream
await instance.items.upload("doc.txt", request.body);
```

#### Upload with metadata

Attach [custom metadata](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/) to a document for filtering in search queries. Custom metadata fields must be defined on the instance first using the [update()](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/#update) method or at creation time.

**TypeScript**

```ts
await instance.items.upload("guide.pdf", pdfBuffer, {
  metadata: {
    category: "onboarding",
    language: "en",
    version: "2.0",
  },
});
```

#### Parameters

| Parameter        | Type                                   | Required | Description                                                                                                                                                            |
| ---------------- | -------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name             | string                                 | Yes      | The filename for the uploaded document. Used as the item key.                                                                                                          |
| content          | ReadableStream, ArrayBuffer, or string | Yes      | The document content. Maximum file size is 4 MB. Pass a string for plain text or markdown, an ArrayBuffer for binary files, or a ReadableStream for streaming uploads. |
| options.metadata | Record<string, string>                 | No       | Custom metadata key-value pairs to attach to the item. Use for filtering in search queries. Maximum 5 fields per instance.                                             |

#### Response

| Field | Type   | Description                      |
| ----- | ------ | -------------------------------- |
| id    | string | The unique item identifier.      |
| key   | string | The filename or key of the item. |

### `items.uploadAndPoll()`

Uploads a document and polls until processing completes or the timeout is reached. Use this when you need to search the document immediately after upload.

**TypeScript**

```ts
// Wait for a specific document to finish indexing before searching
const item = await instance.items.uploadAndPoll(
  "handbook.txt",
  handbookContent,
);
console.log(`handbook.txt status: ${item.status}`); // "completed"


// Now search across all uploaded documents
const results = await instance.search({
  messages: [{ role: "user", content: "password reset policy" }],
});
```

#### Parameters

Same as [items.upload()](#parameters), with additional polling options:

| Parameter              | Type   | Required | Description                                                                          |
| ---------------------- | ------ | -------- | ------------------------------------------------------------------------------------ |
| options.pollIntervalMs | number | No       | How often to check the item status, in milliseconds. Defaults to 1000.               |
| options.timeoutMs      | number | No       | Maximum time to wait for processing to complete, in milliseconds. Defaults to 30000. |

#### Response

Returns the full item object after polling completes:

| Field          | Type   | Description                                                                  |
| -------------- | ------ | ---------------------------------------------------------------------------- |
| id             | string | The unique item identifier.                                                  |
| key            | string | The filename or key of the item.                                             |
| status         | string | The processing status: queued, running, completed, error, skipped, outdated. |
| chunks\_count  | number | Number of chunks created from the document.                                  |
| file\_size     | number | Size of the uploaded file in bytes.                                          |
| metadata       | object | Item metadata including filename, folder, and timestamp.                     |
| source\_id     | string | The source identifier (for example, builtin for uploaded files).             |
| created\_at    | string | Timestamp of when the item was created.                                      |
| last\_seen\_at | string | Timestamp of when the item was last seen during indexing.                    |

### `items.list()`

Returns a paginated list of items in the instance.

**TypeScript**

```ts
const { result, result_info } = await instance.items.list();


for (const item of result) {
  console.log(`${item.key} (${item.status})`);
}
// result_info.total_count contains the total number of items
```

#### Parameters

| Parameter | Type   | Required | Description                                                                           |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------- |
| page      | number | No       | The page number to return. Defaults to 1.                                             |
| per\_page | number | No       | The number of items per page. Defaults to 20. Maximum 50.                             |
| status    | string | No       | Filter by processing status: queued, running, completed, error, skipped, or outdated. |
| sort\_by  | string | No       | Sort order for items: status (default) or modified\_at.                               |
| search    | string | No       | Search items by text content.                                                         |
| source    | string | No       | Filter by source identifier (for example, builtin for uploaded files).                |

#### Response

| Field                     | Type   | Description                                                                  |
| ------------------------- | ------ | ---------------------------------------------------------------------------- |
| result                    | array  | Array of item objects.                                                       |
| result\[\].id             | string | The unique item identifier.                                                  |
| result\[\].key            | string | The filename or key of the item.                                             |
| result\[\].status         | string | The processing status: queued, running, completed, error, skipped, outdated. |
| result\[\].chunks\_count  | number | Number of chunks created from the document.                                  |
| result\[\].file\_size     | number | Size of the uploaded file in bytes.                                          |
| result\[\].metadata       | object | Item metadata including filename, folder, and timestamp.                     |
| result\[\].source\_id     | string | The source identifier (for example, builtin for uploaded files).             |
| result\[\].created\_at    | string | Timestamp of when the item was created.                                      |
| result\[\].last\_seen\_at | string | Timestamp of when the item was last seen during indexing.                    |
| result\_info              | object | Pagination metadata.                                                         |
| result\_info.count        | number | Number of items in the current page.                                         |
| result\_info.total\_count | number | Total number of items in the instance.                                       |
| result\_info.page         | number | The current page number.                                                     |
| result\_info.per\_page    | number | Items per page.                                                              |

### `items.delete()`

Deletes an item and its indexed chunks.

**TypeScript**

```ts
await instance.items.delete("item-id-123");
```

#### Parameters

| Parameter | Type   | Required | Description                                  |
| --------- | ------ | -------- | -------------------------------------------- |
| itemId    | string | Yes      | The unique identifier of the item to delete. |

#### Response

Returns `void`. Throws an error if the item does not exist.

### `items.get()`

Returns a handle to a specific item for retrieving its status or downloading the original file.

#### `items.get().info()`

Returns the status and metadata of a specific item.

**TypeScript**

```ts
const itemInfo = await instance.items.get("item-id-123").info();
```

##### Parameters

| Parameter | Type   | Required | Description                        |
| --------- | ------ | -------- | ---------------------------------- |
| itemId    | string | Yes      | The unique identifier of the item. |

##### Response

| Field          | Type   | Description                                                                  |
| -------------- | ------ | ---------------------------------------------------------------------------- |
| id             | string | The unique item identifier.                                                  |
| key            | string | The filename or key of the item.                                             |
| status         | string | The processing status: queued, running, completed, error, skipped, outdated. |
| chunks\_count  | number | Number of chunks created from the document.                                  |
| file\_size     | number | Size of the uploaded file in bytes.                                          |
| metadata       | object | Item metadata including filename, folder, and timestamp.                     |
| source\_id     | string | The source identifier (for example, builtin for uploaded files).             |
| created\_at    | string | Timestamp of when the item was created.                                      |
| last\_seen\_at | string | Timestamp of when the item was last seen during indexing.                    |

#### `items.get().download()`

Downloads the original source file for an item.

**TypeScript**

```ts
const file = await instance.items.get("item-id-123").download();
// file.body is a ReadableStream
```

##### Parameters

| Parameter | Type   | Required | Description                        |
| --------- | ------ | -------- | ---------------------------------- |
| itemId    | string | Yes      | The unique identifier of the item. |

##### Response

| Field       | Type           | Description                                               |
| ----------- | -------------- | --------------------------------------------------------- |
| filename    | string         | The original filename.                                    |
| contentType | string         | The MIME type of the file (for example, application/pdf). |
| size        | number         | The file size in bytes.                                   |
| body        | ReadableStream | A readable stream of the file contents.                   |

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/items/workers-binding/#page","headline":"Workers binding · Cloudflare AI Search docs","description":"Upload, list, and manage documents in AI Search instances using the Items Workers binding.","url":"https://developers.cloudflare.com/ai-search/api/items/workers-binding/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/items/","name":"Items"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/items/workers-binding/","name":"Workers binding"}}]}
```

---

---
title: Metadata filter (legacy)
description: Reference for the legacy AutoRAG metadata filter format used with the previous REST API.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Metadata filter (legacy)

This page documents the filter format used by the legacy AutoRAG REST API. For the new AI Search REST API filter syntax, refer to [Metadata filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).

## Comparison filter

Compare a metadata attribute (for example, `folder` or `timestamp`) with a target value:

**JavaScript**

```js
filters: {
  type: "eq",
  key: "folder",
  value: "customer-a/"
}
```

### Operators

| Operator | Description              |
| -------- | ------------------------ |
| eq       | Equals                   |
| ne       | Not equals               |
| gt       | Greater than             |
| gte      | Greater than or equal to |
| lt       | Less than                |
| lte      | Less than or equal to    |

## Compound filter

Combine multiple comparison filters with a logical operator:

**JavaScript**

```js
filters: {
  type: "and",
  filters: [
    { type: "eq", key: "folder", value: "customer-a/" },
    { type: "gte", key: "timestamp", value: "1735689600000" }
  ]
}
```

The available compound operators are `and` and `or`.

### Limitations

* No nested combinations of `and` and `or`. You can only use one compound operator at a time.
* When using `or`, only the `eq` operator is allowed and all conditions must filter on the same key.

## "Starts with" filter for folders

To filter for all files within a folder and its subfolders, use a compound filter with range operators.

For example, consider this file structure:

* customer-a \- profile.md - contracts - property - contract-1.pdf

Using `{ type: "eq", key: "folder", value: "customer-a/" }` only matches files directly in that folder (like `profile.md`), not files in subfolders.

To match all files starting with `customer-a/`, use a compound filter:

**JavaScript**

```js
filters: {
  type: "and",
  filters: [
    { type: "gt", key: "folder", value: "customer-a//" },
    { type: "lte", key: "folder", value: "customer-a/z" }
  ]
}
```

This filter matches all paths starting with `customer-a/` by using:

* `gt` with `customer-a//` to include paths greater than the `/` ASCII character
* `lte` with `customer-a/z` to include paths up to and including the lowercase `z` ASCII character

## Related

* [Metadata filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) \- New AI Search REST API filter format
* [Migrate from AutoRAG Search API](https://developers.cloudflare.com/ai-search/api/migration/rest-api/) \- Migration guide with before/after examples

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/migration/autorag-filter-format/#page","headline":"Metadata filter (legacy) · Cloudflare AI Search docs","description":"Reference for the legacy AutoRAG metadata filter format used with the previous REST API.","url":"https://developers.cloudflare.com/ai-search/api/migration/autorag-filter-format/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-17","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/migration/","name":"API Migration"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/migration/autorag-filter-format/","name":"Metadata filter (legacy)"}}]}
```

---

---
title: REST API migration
description: Migrate from the legacy AutoRAG REST API endpoints to the new AI Search API endpoints.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# REST API migration

The [AutoRAG API endpoints](https://developers.cloudflare.com/api/resources/autorag/) are the legacy REST API for AI Search. They will continue to work, but all new features and improvements are only available through the new [AI Search API endpoints](https://developers.cloudflare.com/ai-search/api/search/rest-api/).

## Endpoint changes

The legacy AutoRAG API endpoints under `/autorag/rags/` have been replaced by new endpoints under `/ai-search/instances/`.

| Description      | New endpoint                                 | Reference                                                                                                                       |
| ---------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Chat completions | /ai-search/instances/{name}/chat/completions | [API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/chat%5Fcompletions/) |
| Search           | /ai-search/instances/{name}/search           | [API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/search/)             |

The new API also includes endpoints for [instance management](https://developers.cloudflare.com/ai-search/api/instances/rest-api/), [items](https://developers.cloudflare.com/ai-search/api/items/rest-api/), and [namespace-level search](https://developers.cloudflare.com/ai-search/api/search/rest-api/#cross-instance-search-and-chat) that are not available in the legacy API. For the legacy endpoints, refer to the [AutoRAG API reference](https://developers.cloudflare.com/api/resources/autorag/).

## API token permissions

The legacy AutoRAG endpoints used the **AutoRAG** API token permission. The new AI Search endpoints require the **AI Search** permission instead, so update the permissions on the token you use to call the API. We recommend using [account API tokens](https://developers.cloudflare.com/fundamentals/api/get-started/account-owned-tokens/), which are owned by the account rather than a single user, and adding the **AI Search** permission found under **AI & Machine Learning** \> **AI Search**.

### Create a new token

1. In the Cloudflare dashboard, go to **Manage Account** \> **API Tokens**.
2. Select **Create Token**, then start a custom token.
3. Enter a name for the token.
4. Add a permission policy and select **AI & Machine Learning** \> **AI Search**, then choose the access level you need. AI Search offers **Read**, **Run**, and **Edit** access.
5. (Optional) Set client IP address filtering and a token expiration.
6. Create the token and copy its value.

### Edit an existing token

1. In the Cloudflare dashboard, go to **Manage Account** \> **API Tokens**.
2. Select the token you want to update.
3. Add or update a permission policy to include **AI & Machine Learning** \> **AI Search** with the access level you need, then save.

For the full token creation flow, refer to [Create API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/).

## Chat completions

How to migrate from the AutoRAG `/ai-search` endpoint to the new `/chat/completions` endpoint:

**Before (AutoRAG API):**

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/autorag/rags/<INSTANCE_NAME>/ai-search" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -d '{
    "query": "What is Cloudflare?"
  }'
```

**After (AI Search API):**

The new API uses the `messages` array format.

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -d '{
    "messages": [
      {
        "content": "What is Cloudflare?",
        "role": "user"
      }
    ]
  }'
```

## Search

How to migrate from the AutoRAG `/search` endpoint to the new `/search` endpoint:

**Before (AutoRAG API):**

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/autorag/rags/<INSTANCE_NAME>/search" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -d '{
    "query": "What is Cloudflare?"
  }'
```

**After (AI Search API):**

The new API uses the `messages` array format. The `query` string format is also supported.

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/search" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -d '{
    "messages": [
      {
        "content": "What is Cloudflare?",
        "role": "user"
      }
    ]
  }'
```

## Streaming behavior changes

In the old AutoRAG API, when `stream` was set to `true`, you would only receive the streamed response without the retrieved chunks.

In the new AI Search API, streaming responses include the chunks. The retrieved chunks are sent first as a `chunks` event, followed by the streamed response data. This allows you to display the source chunks immediately while streaming the generated response to the user.

## Filter format

The new AI Search REST API uses Vectorize-style metadata filtering, which differs from the AutoRAG API format. Filters are now nested under `ai_search_options.retrieval.filters` in the request body. For full documentation of the old format, refer to [Metadata filter format (legacy)](https://developers.cloudflare.com/ai-search/api/migration/autorag-filter-format/).

### Operator mapping

The filter operators have been renamed to use a `$` prefix:

| AutoRAG API | AI Search API     |
| ----------- | ----------------- |
| eq          | $eq (or implicit) |
| ne          | $ne               |
| gt          | $gt               |
| gte         | $gte              |
| lt          | $lt               |
| lte         | $lte              |
|             | $in (new)         |
|             | $nin (new)        |

### Examples

#### Simple filter

Filter by a single metadata field using implicit equality:

**Before (AutoRAG API):**

```json
{
  "filters": {
    "type": "eq",
    "key": "folder",
    "value": "customer-a/"
  }
}
```

**After (AI Search API):**

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": { "folder": "customer-a/" }
    }
  }
}
```

#### Compound filter (AND)

Combine multiple conditions where all must match:

**Before (AutoRAG API):**

```json
{
  "filters": {
    "type": "and",
    "filters": [
      { "type": "eq", "key": "folder", "value": "customer-a/" },
      { "type": "gte", "key": "timestamp", "value": "1735689600000" }
    ]
  }
}
```

**After (AI Search API):**

```json
{
  "ai_search_options": {
    "retrieval": {
      "filters": {
        "folder": "customer-a/",
        "timestamp": { "$gte": 1735689600 }
      }
    }
  }
}
```

## API references

* [REST API documentation](https://developers.cloudflare.com/ai-search/api/search/rest-api/)
* [Chat Completions API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/chat%5Fcompletions/)
* [Search API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/search/)
* [Legacy AutoRAG API reference](https://developers.cloudflare.com/api/resources/autorag/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/migration/rest-api/#page","headline":"REST API migration · Cloudflare AI Search docs","description":"Migrate from the legacy AutoRAG REST API endpoints to the new AI Search API endpoints.","url":"https://developers.cloudflare.com/ai-search/api/migration/rest-api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/migration/","name":"API Migration"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/migration/rest-api/","name":"REST API migration"}}]}
```

---

---
title: Workers binding migration
description: Upgrade from the legacy env.AI.autorag() binding to the new AI Search Workers bindings.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Workers binding migration

The [env.AI.autorag() binding](https://developers.cloudflare.com/ai-search/api/migration/workers-binding-legacy/) is the legacy API for AI Search. It will continue to work, but all new features and improvements are only available through the new AI Search bindings.

## What changed

Here is a summary of the key differences between the legacy and new bindings:

|                     | Legacy                 | New                                            |
| ------------------- | ---------------------- | ---------------------------------------------- |
| **Wrangler config** | ai binding             | ai\_search or ai\_search\_namespaces binding   |
| **Access pattern**  | env.AI.autorag("name") | env.MY\_INSTANCE or env.AI\_SEARCH.get("name") |
| **Search format**   | query string           | messages array or query string                 |
| **Response format** | data array             | chunks array                                   |

## AI Search bindings

AI Search provides two new bindings:

**Instance binding (`ai_search`)** binds directly to a single instance. This is the simplest migration path from `env.AI.autorag()`.

**JSONC**

```jsonc
// wrangler.jsonc
{
  "ai_search": [
    {
      "binding": "MY_SEARCH",
      "instance_name": "my-instance",
    },
  ],
}
```

**Namespace binding (`ai_search_namespaces`)** gives you access to all instances within a namespace. Use this if you need dynamic instance management, cross-instance search, or the Items API.

**JSONC**

```jsonc
// wrangler.jsonc
{
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
    },
  ],
}
```

For more details on the difference, refer to [Namespaces](https://developers.cloudflare.com/ai-search/concepts/namespaces/).

## Requirements

The new bindings require the following minimum package versions for TypeScript types and local development support.

| Package                   | Minimum version |
| ------------------------- | --------------- |
| @cloudflare/workers-types | 4.20260304.0    |
| wrangler                  | 4.68.1          |

## Step 1: Update Wrangler configuration

Existing instances are in the default namespace. For a simple upgrade path, use the instance binding. For the namespace binding, refer to [AI Search bindings](#ai-search-bindings).

**Before:**

* [  wrangler.jsonc ](#tab-panel-7184)
* [  wrangler.toml ](#tab-panel-7185)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai": {
    "binding": "AI"
  }
}
```

**TOML**

```toml
[ai]
binding = "AI"
```

**After:**

* [  wrangler.jsonc ](#tab-panel-7186)
* [  wrangler.toml ](#tab-panel-7187)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search": [
    {
      "binding": "MY_INSTANCE",
      "instance_name": "my-instance"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search]]
binding = "MY_INSTANCE"
instance_name = "my-instance"
```

## Step 2: Update the type definition

Update the `Env` interface to use the new binding type.

**Before:**

**TypeScript**

```ts
export interface Env {
  AI: Ai;
}
```

**After:**

**TypeScript**

```ts
export interface Env {
  MY_INSTANCE: AiSearchInstance;
}
```

## Step 3: Update search calls

Replace `env.AI.autorag()` calls with the new binding.

**Before:**

**TypeScript**

```ts
const result = await env.AI.autorag("my-instance").search({
  query: "What is Cloudflare?",
});
```

**After:**

**TypeScript**

```ts
const result = await env.MY_INSTANCE.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
});
```

## Step 4: Update response handling

The response shape changed from a `data` array to a `chunks` array.

### Field mapping

| Old field                          | New field                 |
| ---------------------------------- | ------------------------- |
| data\[\]                           | chunks\[\]                |
| data\[\].file\_id                  | chunks\[\].id             |
| data\[\].filename                  | chunks\[\].item.key       |
| data\[\].score                     | chunks\[\].score          |
| data\[\].content\[\].text          | chunks\[\].text           |
| data\[\].attributes.modified\_date | chunks\[\].item.timestamp |

## Streaming behavior changes

In the legacy binding, streaming with `env.AI.autorag().aiSearch({ stream: true })` only returned the streamed response without the retrieved chunks.

The new binding sends the retrieved chunks first as a `chunks` event, followed by the streamed response. This allows you to display source chunks immediately while streaming the generated response.

## Filter format changes

The new binding uses Vectorize-style metadata filtering. Filters are now passed inside `ai_search_options.retrieval.filters`.

| Old format | New format        |
| ---------- | ----------------- |
| eq         | $eq (or implicit) |
| ne         | $ne               |
| gt         | $gt               |
| gte        | $gte              |
| lt         | $lt               |
| lte        | $lte              |
|            | $in (new)         |
|            | $nin (new)        |

### Examples

#### Simple filter

Filter by a single metadata field using implicit equality:

**Before:**

**TypeScript**

```ts
const result = await env.AI.autorag("my-instance").search({
  query: "What is Cloudflare?",
  filters: {
    type: "eq",
    key: "folder",
    value: "customer-a/",
  },
});
```

**After:**

**TypeScript**

```ts
const result = await env.MY_INSTANCE.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      filters: { folder: "customer-a/" },
    },
  },
});
```

#### Compound filter (AND)

Combine multiple conditions where all must match:

**Before:**

**TypeScript**

```ts
const result = await env.AI.autorag("my-instance").search({
  query: "What is Cloudflare?",
  filters: {
    type: "and",
    filters: [
      { type: "eq", key: "folder", value: "customer-a/" },
      { type: "gte", key: "timestamp", value: "1735689600000" },
    ],
  },
});
```

**After:**

**TypeScript**

```ts
const result = await env.MY_INSTANCE.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    retrieval: {
      filters: {
        folder: "customer-a/",
        timestamp: { $gte: 1735689600 },
      },
    },
  },
});
```

## Backwards compatibility

The `env.AI.autorag()` binding will continue to work indefinitely. You do not need to migrate immediately.

For the legacy API reference, refer to [Workers binding (legacy)](https://developers.cloudflare.com/ai-search/api/migration/workers-binding-legacy/).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/migration/workers-binding/#page","headline":"Workers binding migration · Cloudflare AI Search docs","description":"Upgrade from the legacy env.AI.autorag() binding to the new AI Search Workers bindings.","url":"https://developers.cloudflare.com/ai-search/api/migration/workers-binding/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/migration/","name":"API Migration"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/migration/workers-binding/","name":"Workers binding migration"}}]}
```

---

---
title: Workers binding (legacy)
description: Reference for the legacy env.AI.autorag() Workers binding used by earlier AI Search instances.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Workers binding (legacy)

The `env.AI.autorag()` binding is the legacy API for AI Search. It will continue to work, but new projects should use the new AI Search bindings instead. For a step-by-step upgrade guide, refer to [Workers binding migration](https://developers.cloudflare.com/ai-search/api/migration/workers-binding/).

### `aiSearch()`

This method searches for relevant results from your data source and generates a response using your default model and the retrieved context:

**JavaScript**

```js
const answer = await env.AI.autorag("my-autorag").aiSearch({
  query: "How do I train a llama to deliver coffee?",
  model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
  rewrite_query: true,
  max_num_results: 2,
  ranking_options: {
    score_threshold: 0.3,
  },
  reranking: {
    enabled: true,
    model: "@cf/baai/bge-reranker-base",
  },
  stream: true,
});
```

#### Parameters

`query` ` string ` required

The input query.

---

`model` ` string ` optional

The text-generation model used to generate the response for the query. For a list of valid options, check the AI Search generation model settings. Defaults to the generation model selected in the AI Search settings.

---

`system_prompt` ` string ` optional

The system prompt for generating the answer.

---

`rewrite_query` ` boolean ` optional

Rewrites the original query into a search optimized query to improve retrieval accuracy. Defaults to `false`.

---

`max_num_results` ` number ` optional

The maximum number of results that can be returned from the Vectorize database. Defaults to `10`. Must be between `1` and `50`.

---

`ranking_options` ` object ` optional

Configurations for customizing result ranking. Defaults to `{}`.

* `score_threshold` ` number ` optional  
  * The minimum match score required for a result to be considered a match. Defaults to `0`. Must be between `0` and `1`.

`reranking` ` object ` optional

Configurations for customizing reranking. Defaults to `{}`.

* `enabled` ` boolean ` optional

  * Enables or disables reranking, which reorders retrieved results based on semantic relevance using a reranking model. Defaults to `false`.
* `model` ` string ` optional

  * The reranking model to use when reranking is enabled.

`stream` ` boolean ` optional

Returns a stream of results as they are available. Defaults to `false`.

`filters` ` object ` optional

Narrow down search results based on metadata, like folder and date, so only relevant content is retrieved. For more details, refer to [Metadata filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).

#### Response

This is the response structure without `stream` enabled.

```json
{
  "object": "vector_store.search_results.page",
  "search_query": "How do I train a llama to deliver coffee?",
  "response": "To train a llama to deliver coffee:\n\n1. **Build trust** — Llamas appreciate patience (and decaf).\n2. **Know limits** — Max 3 cups per llama, per `llama-logistics.md`.\n3. **Use voice commands** — Start with \"Espresso Express!\"\n4.",
  "data": [
    {
      "file_id": "llama001",
      "filename": "llama/logistics/llama-logistics.md",
      "score": 0.45,
      "attributes": {
        "modified_date": 1735689600000,
        "folder": "llama/logistics/"
      },
      "content": [
        {
          "id": "llama001",
          "type": "text",
          "text": "Llamas can carry 3 drinks max."
        }
      ]
    },
    {
      "file_id": "llama042",
      "filename": "llama/llama-commands.md",
      "score": 0.4,
      "attributes": {
        "modified_date": 1735689600000,
        "folder": "llama/"
      },
      "content": [
        {
          "id": "llama042",
          "type": "text",
          "text": "Start with basic commands like 'Espresso Express!' Llamas love alliteration."
        }
      ]
    }
  ],
  "has_more": false,
  "next_page": null
}
```

### `search()`

This method searches for results from your corpus and returns the relevant results:

**JavaScript**

```js
const answer = await env.AI.autorag("my-autorag").search({
  query: "How do I train a llama to deliver coffee?",
  rewrite_query: true,
  max_num_results: 2,
  ranking_options: {
    score_threshold: 0.3,
  },
  reranking: {
    enabled: true,
    model: "@cf/baai/bge-reranker-base",
  },
});
```

#### Parameters

`messages` ` array ` required

An array of message objects. Each message has:

* `content` ` string ` \- The search query content.
* `role` ` string ` \- The role: `user`, `system`, or `assistant`.

---

`ai_search_options` ` object ` optional

Per-request overrides for retrieval and model behavior. Supports the following nested options:

* `retrieval.filters` ` object ` \- Narrow down search results based on metadata. Refer to [Metadata filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) for syntax and examples.
* `retrieval.max_num_results` ` number ` \- Maximum number of chunks to return. Defaults to `10`, maximum `50`.
* `retrieval.retrieval_type` ` string ` \- One of `vector`, `keyword`, or `hybrid`.
* `retrieval.match_threshold` ` number ` \- Minimum similarity score (0-1). Defaults to `0.4`.
* `cache.enabled` ` boolean ` \- Override the instance-level cache setting for this request.
* `reranking.enabled` ` boolean ` \- Override the instance-level reranking setting for this request.

---

For the full list of optional parameters, refer to the [Search API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/search/).

#### Response

```json
{
  "object": "vector_store.search_results.page",
  "search_query": "How do I train a llama to deliver coffee?",
  "data": [
    {
      "file_id": "llama001",
      "filename": "llama/logistics/llama-logistics.md",
      "score": 0.45,
      "attributes": {
        "modified_date": 1735689600000,
        "folder": "llama/logistics/"
      },
      "content": [
        {
          "id": "llama001",
          "type": "text",
          "text": "Llamas can carry 3 drinks max."
        }
      ]
    },
    {
      "file_id": "llama042",
      "filename": "llama/llama-commands.md",
      "score": 0.4,
      "attributes": {
        "modified_date": 1735689600000,
        "folder": "llama/"
      },
      "content": [
        {
          "id": "llama042",
          "type": "text",
          "text": "Start with basic commands like 'Espresso Express!' Llamas love alliteration."
        }
      ]
    }
  ],
  "has_more": false,
  "next_page": null
}
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/migration/workers-binding-legacy/#page","headline":"Workers binding (legacy) · Cloudflare AI Search docs","description":"Reference for the legacy env.AI.autorag() Workers binding used by earlier AI Search instances.","url":"https://developers.cloudflare.com/ai-search/api/migration/workers-binding-legacy/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/migration/","name":"API Migration"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/migration/workers-binding-legacy/","name":"Workers binding (legacy)"}}]}
```

---

---
title: MCP
description: Expose AI Search content to AI agents through the Model Context Protocol (MCP) endpoint.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# MCP

The Model Context Protocol (MCP) endpoint allows AI agents to discover and interact with your AI Search content. This endpoint follows the [MCP specification ↗](https://modelcontextprotocol.io/) and provides tools for querying your indexed content.

## Prerequisites

Enable public endpoints for your AI Search instance:

1. Go to **AI Search** in the Cloudflare dashboard. [ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your AI Search instance.
3. Go to **Settings** \> **Public Endpoint**.
4. Turn on **Enable Public Endpoint**.
5. Copy the public endpoint URL.

## Available tools

The AI Search MCP endpoint exposes a `search` tool that queries your indexed content.

| Tool   | Description                           |
| ------ | ------------------------------------- |
| search | Finds exactly what you're looking for |

You can customize this in your AI Search instance settings. For more details, refer to [Public endpoint configuration](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/).

## Test the MCP endpoint

Send a request to the `/mcp` endpoint with the `Accept: application/json, text/event-stream` header:

```bash
curl https://<INSTANCE_ID>.search.ai.cloudflare.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "search",
      "arguments": {
        "query": "How do I configure AI Search?"
      }
    }
  }'
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/search/mcp/#page","headline":"MCP · Cloudflare AI Search docs","description":"Expose AI Search content to AI agents through the Model Context Protocol (MCP) endpoint.","url":"https://developers.cloudflare.com/ai-search/api/search/mcp/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/search/","name":"Search"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/search/mcp/","name":"MCP"}}]}
```

---

---
title: Public endpoint
description: Integrate AI Search into public-facing applications using unauthenticated public endpoints.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Public endpoint

AI Search public endpoints allow you to expose AI Search capabilities without requiring authentication. This enables you to integrate AI Search into public-facing applications or share it with external users.

For pre-built search and chat components you can embed on your website using the public endpoints, refer to [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/).

## Prerequisites

Enable public endpoints for your AI Search instance:

1. Go to **AI Search** in the Cloudflare dashboard. [ Go to **AI Search** ](https://dash.cloudflare.com/?to=/:account/ai/ai-search)
2. Select your AI Search instance.
3. Go to **Settings** \> **Public Endpoint**.
4. Turn on **Enable Public Endpoint**.
5. Copy the public endpoint URL.

For configuration options like rate limiting and CORS, refer to [Public endpoint configuration](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/).

## Chat completions

The `/chat/completions` endpoint searches your data source and generates a response using the model and retrieved context. It uses the same OpenAI-compatible format as the [REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/#chat-completions).

```bash
curl https://<INSTANCE_ID>.search.ai.cloudflare.com/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "content": "How do I configure AI Search?",
        "role": "user"
      }
    ]
  }'
```

For the full list of options, refer to the [Chat Completions API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/chat%5Fcompletions/).

## Search

The `/search` endpoint returns relevant chunks from your data source without generating a response. It uses the same format as the [REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/#search).

```bash
curl https://<INSTANCE_ID>.search.ai.cloudflare.com/search \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "content": "How do I configure AI Search?",
        "role": "user"
      }
    ]
  }'
```

For the full list of options, refer to the [Search API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/search/).

## Next steps

* [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/) \- Add pre-built search and chat components to your website.
* [MCP](https://developers.cloudflare.com/ai-search/api/search/mcp/) \- Connect AI agents using the Model Context Protocol.
* [Public endpoint configuration](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/) \- Configure rate limiting, CORS, and security settings.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/search/public-endpoint/#page","headline":"Public endpoint · Cloudflare AI Search docs","description":"Integrate AI Search into public-facing applications using unauthenticated public endpoints.","url":"https://developers.cloudflare.com/ai-search/api/search/public-endpoint/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/search/","name":"Search"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/search/public-endpoint/","name":"Public endpoint"}}]}
```

---

---
title: REST API
description: Query AI Search instances over HTTP using the REST API for search and chat completions.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# REST API

Use the AI Search REST API to query your AI Search instances over HTTP.

Note

The previous [AutoRAG API endpoints](https://developers.cloudflare.com/api/resources/autorag/) are no longer recommended for use. Refer to [Migrate from AutoRAG REST API](https://developers.cloudflare.com/ai-search/api/migration/rest-api/) for details.

## Authentication

All requests require an API token with **AI Search:Edit** and **AI Search:Run** permissions.

1. In the Cloudflare dashboard, go to **My Profile** \> **API Tokens**.  
[ Go to **API Tokens** ](https://dash.cloudflare.com/profile/api-tokens)
2. Select **Create Token**.
3. Select **Create Custom Token**.
4. Enter a **Token name**, for example `AI Search Manager`.
5. Under **Permissions**, add two permissions:

  * **Account** \> **AI Search:Edit**
  * **Account** \> **AI Search:Run**
6. Select **Continue to summary**, then select **Create Token**.
7. Copy and save the token value. This is your `API_TOKEN`.

Include the token in the `Authorization` header for all requests:

```txt
Authorization: Bearer <API_TOKEN>
```

## Search and chat

AI Search provides two APIs for querying an instance. Both use an OpenAI-compatible `messages` format.

* **Search** returns relevant content chunks. Use this when you want to handle generation yourself or display results directly.
* **Chat completions** retrieves content and generates a response in one call.

### API paths

AI Search APIs are available at two base paths:

| Path                                                                     | Description                                                                                                  |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| /accounts/{account\_id}/ai-search/instances/{id}/                        | Operates on a specific instance                                                                              |
| /accounts/{account\_id}/ai-search/namespaces/{namespace}/instances/{id}/ | Operates on instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) |

The following operations are the same for both paths. For the namespace-scoped API, refer to the [Namespace API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/).

### Search

Search a specific instance. The search endpoint also accepts a `query` string parameter. For the full specification, refer to the [Search API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/search/).

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/search" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "content": "What is Cloudflare?",
        "role": "user"
      }
    ]
  }'
```

### Chat completions

Generate a response from a specific instance. For the full specification, refer to the [Chat completions API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/instances/methods/chat%5Fcompletions/).

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances/<INSTANCE_NAME>/chat/completions" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "content": "What is Cloudflare?",
        "role": "user"
      }
    ]
  }'
```

#### Streaming

Set `stream` to `true` to receive responses as Server-Sent Events (SSE). The retrieved chunks are sent first as a `chunks` event, followed by the streamed response.

```txt
event: chunks
data: [{"id":"chunk-001","type":"text","score":0.85,"text":"...","item":{"key":"about-cloudflare.md","timestamp":1775925540000},"scoring_details":{"vector_score":0.85}}]


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" document"}}]}


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" you provided doesn"}}]}


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"'t contain"}}]}


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" information"}}]}


data: [DONE]
```

## Cross-instance search and chat

The search and chat completions APIs are also available at the namespace level. These work the same as the instance endpoints, but you pass an `instance_ids` array to specify which instances to query. Each chunk in the response includes an `instance_id` field identifying which instance it came from. For the full specification, refer to the [Namespace API reference](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/).

```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/namespaces/<NAMESPACE>/search" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "content": "What is Cloudflare?"
      }
    ],
    "ai_search_options": {
      "instance_ids": ["product-docs", "customer-abc123"]
    }
  }'
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/search/rest-api/#page","headline":"REST API · Cloudflare AI Search docs","description":"Query AI Search instances over HTTP using the REST API for search and chat completions.","url":"https://developers.cloudflare.com/ai-search/api/search/rest-api/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/search/","name":"Search"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/search/rest-api/","name":"REST API"}}]}
```

---

---
title: Workers binding
description: Search and chat with AI Search instances from a Cloudflare Worker using the Workers binding.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Workers binding

[Workers](https://developers.cloudflare.com/workers/) provides a serverless execution environment that allows you to create new applications or augment existing ones. Use a [Workers binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/) to search and chat with your AI Search instances from a Cloudflare Worker.

Note

The previous `env.AI.autorag()` binding is no longer recommended for use. Refer to [Workers binding migration](https://developers.cloudflare.com/ai-search/api/migration/workers-binding/) for details.

## Configure the binding

To use AI Search with Workers, you must create an AI Search binding. You create bindings by updating your [Wrangler configuration](https://developers.cloudflare.com/workers/wrangler/configuration/). AI Search provides two types of bindings:

* Namespace binding: `ai_search_namespaces`
* Instance binding: `ai_search`

### Namespace binding

Access all instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/). You can get, create, list, and delete instances at runtime.

* [  wrangler.jsonc ](#tab-panel-7188)
* [  wrangler.toml ](#tab-panel-7189)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "my-namespace"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "my-namespace"
```

| Field     | Type    | Required | Description                                                                                                                                                                                                                   |
| --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| binding   | string  | Yes      | The variable name available on env. For example, "AI\_SEARCH" makes it accessible as env.AI\_SEARCH.                                                                                                                          |
| namespace | string  | Yes      | The [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) to bind to. A default namespace is created automatically for every account. If the namespace does not exist, Wrangler creates it on deploy. |
| remote    | boolean | No       | Set to true for local development with wrangler dev.                                                                                                                                                                          |

### Instance binding

Bind directly to a single instance in the `default` namespace. Use this when you know which instance you need at deploy time.

* [  wrangler.jsonc ](#tab-panel-7190)
* [  wrangler.toml ](#tab-panel-7191)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_date": "2026-03-27",
  "ai_search": [
    {
      "binding": "MY_SEARCH",
      "instance_name": "my-instance"
    }
  ]
}
```

**TOML**

```toml
compatibility_date = "2026-03-27"


[[ai_search]]
binding = "MY_SEARCH"
instance_name = "my-instance"
```

| Field          | Type    | Required | Description                                                                                          |
| -------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------- |
| binding        | string  | Yes      | The variable name available on env. For example, "MY\_SEARCH" makes it accessible as env.MY\_SEARCH. |
| instance\_name | string  | Yes      | The name of the AI Search instance. Must exist in the default namespace at deploy time.              |
| remote         | boolean | No       | Set to true for local development with wrangler dev.                                                 |

## Instance methods

The following methods are available on both the `ai_search_namespaces` and `ai_search` bindings. With the namespace binding, call methods on the handle returned by `get()`. With the instance binding, call methods directly on the binding (for example, `env.MY_SEARCH.search()`).

The examples below use the namespace binding.

### `search()`

Search for relevant content chunks from your indexed data source. Returns scored chunks with source references.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const results = await instance.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
});
```

#### Parameters

`messages` ` array ` required

An array of message objects representing the conversation. Each message has a `role` and `content` field.

* `role` ` string ` required

  * The role of the message sender. Valid values: `system`, `developer`, `user`, `assistant`, `tool`.
* `content` ` string ` required

  * The content of the message.

---

`query` ` string ` optional

A simple text query string. Alternative to `messages`. Provide either `query` or `messages`, not both.

---

`ai_search_options` ` object ` optional

Configuration options for the search operation.

* `retrieval` ` object ` optional

  * `retrieval_type` ` string ` optional

    * The type of retrieval to perform. Valid values: `vector`, `keyword`, `hybrid`. Defaults to `hybrid`.
  * `match_threshold` ` number ` optional

    * The minimum match score required for a result to be considered a match. Must be between `0` and `1`. Defaults to `0.4`.
  * `max_num_results` ` integer ` optional

    * The maximum number of results to return. Must be between `1` and `50`. Defaults to `10`.
  * `filters` ` object ` optional

    * Filter search results based on metadata. Supports comparison filters (`eq`, `ne`, `gt`, `gte`, `lt`, `lte`) and compound filters (`and`, `or`). For more details, refer to [Metadata filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).
  * `context_expansion` ` integer ` optional

    * The number of surrounding chunks to include for additional context. Must be between `0` and `3`. Defaults to `0`.
  * `fusion_method` ` string ` optional

    * Controls how vector and keyword scores are combined when using hybrid retrieval. Valid values: `rrf` (Reciprocal Rank Fusion), `max` (takes the maximum score). Defaults to the instance-level setting.
  * `keyword_match_mode` ` string ` optional

    * Controls how keyword (BM25) matching selects candidate documents. `and` requires all terms to match. `or` requires any term to match. Defaults to `and`.
  * `boost_by` ` array ` optional

    * Boost results by metadata fields. Maximum 3 items. Each item has:  
      * `field` ` string ` required \- The metadata field name to boost by (for example, `timestamp`). Maximum 64 characters.
      * `direction` ` string ` optional \- The boost direction. Valid values: `asc`, `desc`, `exists`, `not_exists`. Defaults to `asc` for numeric fields and `exists` for text fields.
  * `metadata_only` ` boolean ` optional

    * Return only metadata for each chunk without the text content.
  * `return_on_failure` ` boolean ` optional

    * Whether to return partial results if some processing steps fail. Defaults to `true`.
* `query_rewrite` ` object ` optional

  * `enabled` ` boolean ` optional

    * Rewrites the query to improve retrieval accuracy. Defaults to `false`.
  * `model` ` string ` optional

    * The model to use for query rewriting.
  * `rewrite_prompt` ` string ` optional

    * A custom prompt to guide query rewriting.
* `reranking` ` object ` optional

  * `enabled` ` boolean ` optional

    * Reorders retrieved results based on semantic relevance using a reranking model. Defaults to `false`.
  * `model` ` string ` optional

    * The reranking model to use. Valid value: `@cf/baai/bge-reranker-base`.
  * `match_threshold` ` number ` optional

    * The minimum score for reranked results. Must be between `0` and `1`. Defaults to `0.4`.
* `cache` ` object ` optional

  * `enabled` ` boolean ` optional

    * Override the instance-level cache setting for this request.
  * `cache_threshold` ` string ` optional

    * The similarity threshold for cache hits. Valid values: `super_strict_match`, `close_enough`, `flexible_friend`, `anything_goes`.

#### Response

The response contains the following fields:

| Field                                        | Type   | Description                                                                          |
| -------------------------------------------- | ------ | ------------------------------------------------------------------------------------ |
| search\_query                                | string | The query used for the search, which may be rewritten if query rewriting is enabled. |
| chunks                                       | array  | An array of matching content chunks.                                                 |
| chunks\[\].id                                | string | The unique identifier for the chunk.                                                 |
| chunks\[\].type                              | string | The type of content, typically text.                                                 |
| chunks\[\].score                             | number | The overall match score between 0 and 1.                                             |
| chunks\[\].text                              | string | The text content of the chunk.                                                       |
| chunks\[\].item                              | object | Information about the source item.                                                   |
| chunks\[\].item.key                          | string | The file path or URL of the source document.                                         |
| chunks\[\].item.timestamp                    | number | Unix timestamp of when the item was last modified.                                   |
| chunks\[\].item.metadata                     | object | Custom metadata associated with the source item.                                     |
| chunks\[\].scoring\_details                  | object | Breakdown of how the chunk was scored.                                               |
| chunks\[\].scoring\_details.vector\_score    | number | The semantic similarity score (0 to 1).                                              |
| chunks\[\].scoring\_details.keyword\_score   | number | The keyword (BM25) match score. Present when using hybrid or keyword retrieval.      |
| chunks\[\].scoring\_details.keyword\_rank    | number | The keyword rank position.                                                           |
| chunks\[\].scoring\_details.vector\_rank     | number | The vector rank position.                                                            |
| chunks\[\].scoring\_details.reranking\_score | number | The reranking score (0 to 1). Present when reranking is enabled.                     |
| chunks\[\].scoring\_details.fusion\_method   | string | The fusion method used (rrf or max). Present when using hybrid retrieval.            |

### `chatCompletions()`

Generate chat completions using your AI Search instance as context. This method retrieves relevant content and uses it to generate a response.

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const response = await instance.chatCompletions({
  messages: [
    { role: "system", content: "You are a helpful documentation assistant." },
    { role: "user", content: "What is Cloudflare?" },
  ],
  model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
  ai_search_options: {
    retrieval: {
      max_num_results: 5,
    },
    query_rewrite: {
      enabled: true,
    },
  },
});
```

#### Stream responses

Set `stream: true` to receive responses as Server-Sent Events (SSE) as they are generated:

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");


const stream = await instance.chatCompletions({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  stream: true,
});


return new Response(stream, {
  headers: {
    "content-type": "text/event-stream",
    "cache-control": "no-cache",
  },
});
```

When `stream` is enabled, the method returns a `ReadableStream` of SSE events. Each event contains a JSON object with `choices[0].delta.content` for incremental text. The stream ends with a `data: [DONE]` event.

#### Parameters

`messages` ` array ` required

An array of message objects representing the conversation. Each message has a `role` and `content` field.

* `role` ` string ` required

  * The role of the message sender. Valid values: `system`, `developer`, `user`, `assistant`, `tool`.
* `content` ` string ` required

  * The content of the message.

---

`model` ` string ` optional

The text-generation model used to generate responses. Defaults to the generation model configured in the AI Search instance settings. For a list of supported models, refer to [Supported models](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).

---

`stream` ` boolean ` optional

Returns a stream of results as they are generated. When enabled, returns a `Response` object with a readable stream. Defaults to `false`.

---

`ai_search_options` ` object ` optional

Configuration options for the search and generation operation.

* `retrieval` ` object ` optional

  * `retrieval_type` ` string ` optional

    * The type of retrieval to perform. Valid values: `vector`, `keyword`, `hybrid`. Defaults to `hybrid`.
  * `match_threshold` ` number ` optional

    * The minimum match score required for a result to be considered a match. Must be between `0` and `1`. Defaults to `0.4`.
  * `max_num_results` ` integer ` optional

    * The maximum number of results to return. Must be between `1` and `50`. Defaults to `10`.
  * `filters` ` object ` optional

    * Filter search results based on metadata. Supports comparison filters (`eq`, `ne`, `gt`, `gte`, `lt`, `lte`) and compound filters (`and`, `or`). For more details, refer to [Metadata filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).
  * `context_expansion` ` integer ` optional

    * The number of surrounding chunks to include for additional context. Must be between `0` and `3`. Defaults to `0`.
  * `fusion_method` ` string ` optional

    * Controls how vector and keyword scores are combined when using hybrid retrieval. Valid values: `rrf` (Reciprocal Rank Fusion), `max` (takes the maximum score). Defaults to the instance-level setting.
  * `keyword_match_mode` ` string ` optional

    * Controls how keyword (BM25) matching selects candidate documents. `and` requires all terms to match. `or` requires any term to match. Defaults to `and`.
  * `boost_by` ` array ` optional

    * Boost results by metadata fields. Maximum 3 items. Each item has:  
      * `field` ` string ` required \- The metadata field name to boost by (for example, `timestamp`). Maximum 64 characters.
      * `direction` ` string ` optional \- The boost direction. Valid values: `asc`, `desc`, `exists`, `not_exists`. Defaults to `asc` for numeric fields and `exists` for text fields.
  * `metadata_only` ` boolean ` optional

    * Return only metadata for each chunk without the text content.
  * `return_on_failure` ` boolean ` optional

    * Whether to return partial results if some processing steps fail. Defaults to `true`.
* `query_rewrite` ` object ` optional

  * `enabled` ` boolean ` optional

    * Rewrites the query to improve retrieval accuracy. Defaults to `false`.
  * `model` ` string ` optional

    * The model to use for query rewriting.
  * `rewrite_prompt` ` string ` optional

    * A custom prompt to guide query rewriting.
* `reranking` ` object ` optional

  * `enabled` ` boolean ` optional

    * Reorders retrieved results based on semantic relevance using a reranking model. Defaults to `false`.
  * `model` ` string ` optional

    * The reranking model to use. Valid value: `@cf/baai/bge-reranker-base`.
  * `match_threshold` ` number ` optional

    * The minimum score for reranked results. Must be between `0` and `1`. Defaults to `0.4`.
* `cache` ` object ` optional

  * `enabled` ` boolean ` optional

    * Override the instance-level cache setting for this request.
  * `cache_threshold` ` string ` optional

    * The similarity threshold for cache hits. Valid values: `super_strict_match`, `close_enough`, `flexible_friend`, `anything_goes`.

#### Response (non-streaming)

| Field                       | Type   | Description                                                                         |
| --------------------------- | ------ | ----------------------------------------------------------------------------------- |
| id                          | string | Unique identifier for the completion.                                               |
| object                      | string | Always chat.completion.                                                             |
| created                     | number | Unix timestamp of when the completion was created.                                  |
| model                       | string | The model used to generate the response.                                            |
| choices                     | array  | Array of completion choices.                                                        |
| choices\[\].message.role    | string | Always assistant.                                                                   |
| choices\[\].message.content | string | The generated response text.                                                        |
| choices\[\].finish\_reason  | string | Why the model stopped generating. Typically stop.                                   |
| usage.prompt\_tokens        | number | Number of tokens in the prompt.                                                     |
| usage.completion\_tokens    | number | Number of tokens in the generated response.                                         |
| usage.total\_tokens         | number | Total tokens used.                                                                  |
| chunks                      | array  | The source chunks used as context. Same format as the [search response](#response). |

#### Response (streaming)

When `stream: true`, the method returns a `ReadableStream` of Server-Sent Events. The retrieved chunks are sent first as a `chunks` event, followed by the streamed response.

```txt
event: chunks
data: [{"id":"chunk-001","type":"text","score":0.85,"text":"...","item":{"key":"about-cloudflare.md","timestamp":1775925540000},"scoring_details":{"vector_score":0.85}}]


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" document"}}]}


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" you provided doesn"}}]}


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"'t contain"}}]}


data: {"id":"id-1776072781845","created":1776072781,"model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" information"}}]}


data: [DONE]
```

## Namespace methods

The following methods are only available when using the `ai_search_namespaces` binding. Search and chat across multiple instances in a single call using the namespace handle directly (`env.AI_SEARCH`).

### `search()`

Pass `instance_ids` in `ai_search_options` to specify which instances to query. Results are merged and ranked, and each chunk includes an `instance_id` field identifying which instance it came from.

**TypeScript**

```ts
const results = await env.AI_SEARCH.search({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    instance_ids: ["product-docs", "customer-abc123"],
  },
});
```

#### Parameters

Same as [instance-level search](#parameters), with one additional required field:

| Parameter                         | Type   | Required | Description                                           |
| --------------------------------- | ------ | -------- | ----------------------------------------------------- |
| ai\_search\_options               | object | Yes      | Required for namespace-level search.                  |
| ai\_search\_options.instance\_ids | array  | Yes      | Instance IDs to search across. Minimum 1, maximum 10. |

#### Response

Same as [instance-level search](#response), with additional fields:

| Field                   | Type   | Description                                                                            |
| ----------------------- | ------ | -------------------------------------------------------------------------------------- |
| chunks\[\].instance\_id | string | The instance this chunk came from.                                                     |
| errors                  | array  | Per-instance errors if any instances failed. Each object has instance\_id and message. |

### `chatCompletions()`

Generate chat completions using context retrieved from multiple instances.

**TypeScript**

```ts
const response = await env.AI_SEARCH.chatCompletions({
  messages: [{ role: "user", content: "What is Cloudflare?" }],
  ai_search_options: {
    instance_ids: ["product-docs", "customer-abc123"],
  },
});
```

Streaming is supported with `stream: true`.

#### Parameters

Same as [instance-level chat completions](#parameters-1), with one additional required field:

| Parameter                         | Type   | Required | Description                                           |
| --------------------------------- | ------ | -------- | ----------------------------------------------------- |
| ai\_search\_options               | object | Yes      | Required for namespace-level chat completions.        |
| ai\_search\_options.instance\_ids | array  | Yes      | Instance IDs to search across. Minimum 1, maximum 10. |

#### Response

Same as [instance-level chat completions](#response-non-streaming), with additional fields on each chunk:

| Field                   | Type   | Description                                                                            |
| ----------------------- | ------ | -------------------------------------------------------------------------------------- |
| chunks\[\].instance\_id | string | The instance this chunk came from.                                                     |
| errors                  | array  | Per-instance errors if any instances failed. Each object has instance\_id and message. |

## Local development

Local development is supported by proxying requests to your deployed AI Search instance. Add `remote: true` to your binding configuration to enable local development with `wrangler dev`.

**JSONC**

```jsonc
// wrangler.jsonc
{
  "ai_search": [
    {
      "binding": "MY_SEARCH",
      "instance_name": "my-instance",
      "remote": true,
    },
  ],
}
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/api/search/workers-binding/#page","headline":"Workers binding · Cloudflare AI Search docs","description":"Search and chat with AI Search instances from a Cloudflare Worker using the Workers binding.","url":"https://developers.cloudflare.com/ai-search/api/search/workers-binding/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/api/","name":"API"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/api/search/","name":"Search"}},{"@type":"ListItem","position":5,"item":{"@id":"/ai-search/api/search/workers-binding/","name":"Workers binding"}}]}
```

---

---
title: How AI Search works
description: Understand how AI Search indexes your content and retrieves results using vector and keyword search.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# How AI Search works

AI Search is a managed search service. Connect a website, an R2 bucket, or upload your own documents, and AI Search indexes your content for natural language queries.

AI Search consists of two core processes:

* **Indexing:** An asynchronous process that converts your content into vectors and keyword indexes for search. Indexing runs automatically when you connect a data source or upload files.
* **Querying:** A synchronous process triggered by user queries. It retrieves the most relevant content using vector search, keyword search, or both, and optionally generates a response.

## How indexing works

Indexing begins automatically when you connect a data source or upload files through the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/).

[ Your content e.g. PDF, image ](https://developers.cloudflare.com/ai-search/configuration/data-source/) 

source 

[ Data source Optional R2 bucket ](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/) [ Data source · Browser Run Optional Website ](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) 

[ AI Search · R2 Built-in storage ](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) 

[ Workers AI · toMarkdown() Parsing ](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/) [ AI Search Chunking ](https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/) 

index & store 

[ AI Gateway / Workers AI Optional Embedding ](https://developers.cloudflare.com/ai-search/configuration/models/) [ AI Search · Vectorize Optional Vector index ](https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/) 

[ AI Search Optional Keyword tokenizer ](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) [ AI Search Optional Inverted index ](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) 

Here is what happens during indexing:

1. **Data ingestion:** AI Search reads from your connected data source or receives files uploaded through the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/).
2. **Markdown conversion:** AI Search uses [Workers AI's Markdown Conversion](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/) to convert [supported data types](https://developers.cloudflare.com/ai-search/configuration/data-source/) into structured Markdown. This ensures consistency across diverse file types. For images, Workers AI is used to perform object detection followed by vision-to-language transformation to convert images into Markdown text. Refer to [how images are converted](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/how-it-works/#images) for details.
3. **Chunking:** The extracted text is [chunked](https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/) into smaller pieces to improve retrieval granularity.
4. **Embedding:** Each chunk is embedded using Workers AI's embedding model to transform the content into vectors.
5. **Keyword indexing:** When keyword search is enabled, each chunk is also indexed for BM25 keyword matching.
6. **Storage:** The vectors, keyword index, and content are stored and ready for search.

For instances with a connected data source, AI Search regularly checks for updates and indexes changes automatically. For instances using [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/), new files are indexed as they are uploaded.

## How querying works

Once indexing is complete, AI Search is ready to respond to end-user queries in real time.

Your query 

[ AI Gateway / Workers AI Optional Query rewriting ](https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/) 

hybrid search 

[ AI Gateway / Workers AI Optional Query embedding ](https://developers.cloudflare.com/ai-search/configuration/models/) [ AI Search · Vectorize Optional Vector retrieval ](https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/) 

[ AI Search Optional Query tokenization ](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) [ AI Search · BM25 Optional Keyword retrieval ](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) 

[ AI Search Optional Fusion ](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) 

[ AI Gateway / Workers AI Optional Reranking ](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/) [ AI Search · R2 Chunk content retrieval ](https://developers.cloudflare.com/ai-search/api/search/rest-api/) [ Search result ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/#search) [ AI Gateway / Workers AI Optional Response generation ](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/) [ Chat Completions result ](https://developers.cloudflare.com/ai-search/api/search/workers-binding/#chatcompletions) 

Here is how the querying pipeline works:

1. **Receive query from AI Search API:** The query workflow begins when you send a request to either the AI Search's [Chat Completions](https://developers.cloudflare.com/ai-search/api/search/rest-api/#chat-completions) or [Search](https://developers.cloudflare.com/ai-search/api/search/rest-api/#search) endpoints.
2. **Query rewriting (optional):** AI Search provides the option to [rewrite the input query](https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/) using one of Workers AI's LLMs to improve retrieval quality by transforming the original query into a more effective search query.
3. **Embedding the query:** The rewritten (or original) query is transformed into a vector using the same embedding model used to embed your data.
4. **Vector search:** The query vector is matched against stored vectors to find semantically similar content.
5. **Keyword search (optional):** When hybrid search is enabled, a BM25 keyword search runs in parallel with vector search.
6. **Fusion (optional):** When using hybrid search, vector and keyword results are combined using the configured fusion method.
7. **Reranking (optional):** A cross-encoder model re-scores results by evaluating the query and document together. Refer to [Reranking](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/) for details.
8. **Content retrieval:** The most relevant chunks and their source content are returned. If you are using the Search endpoint, the content is returned at this point.
9. **Response generation:** If you are using the Chat Completions endpoint, a text-generation model generates a response using the retrieved content. Refer to [System prompt](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/) for details.

## When to use AI Search vs. Vectorize

AI Search is built on [Vectorize](https://developers.cloudflare.com/vectorize/) and adds the rest of the search pipeline around it. Use Vectorize when you want to manage vectors yourself, and AI Search when you want managed search over your content.

| Capability              | AI Search                                                  | Vectorize                                        |
| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------ |
| What it is              | Managed, end-to-end search over your content               | A vector database you build on                   |
| You give it             | Files, or a connected data source                          | Vectors you generate yourself                    |
| Chunking and embeddings | Handled for you                                            | You generate and insert them                     |
| Indexing                | Automatic, with continuous sync                            | You upsert and manage vectors                    |
| Retrieval               | Vector and keyword (hybrid), reranking, metadata filtering | Vector similarity search with metadata filtering |
| Generated answers       | Optional, built in                                         | Not included                                     |
| Best when               | You want to add search or RAG quickly                      | You need full control of the retrieval pipeline  |

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/concepts/how-ai-search-works/#page","headline":"How AI Search works · Cloudflare AI Search docs","description":"Understand how AI Search indexes your content and retrieves results using vector and keyword search.","url":"https://developers.cloudflare.com/ai-search/concepts/how-ai-search-works/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-06","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/concepts/how-ai-search-works/","name":"How AI Search works"}}]}
```

---

---
title: Namespaces
description: Group AI Search instances into namespaces and manage them dynamically from a Workers binding.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Namespaces

Every AI Search instance belongs to a **namespace**. A namespace is a logical grouping of instances within your account.

[ Tenant A ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ Tenant B ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ Tenant C ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) 

[ env.AI\_SEARCH.get(id) Worker ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) 

namespace: tenants 

[ AI Search instance tenant-a ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ AI Search instance tenant-b ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) [ AI Search instance tenant-c ](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) 

## Why use namespaces

Common reasons to use namespaces include:

* **Domain separation**: Separate instances by product area, for example `blog`, `support`, and `docs`.
* **Tenant isolation**: Assign each tenant their own namespace so that instance names do not collide across tenants.
* **Agent isolation**: Give each agent its own namespace for independent context management.

For a step-by-step guide to isolating search per tenant, see [Multitenancy](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/).

## Requirements

The namespace binding requires the following minimum package versions for TypeScript types and local development support.

| Package                   | Minimum version |
| ------------------------- | --------------- |
| @cloudflare/workers-types | 4.20260304.0    |
| wrangler                  | 4.68.1          |

## How namespaces work

When you add an `ai_search_namespaces` binding to your Wrangler configuration, you specify which namespace the binding has access to. The binding grants full access to all instances within that namespace. You can get, list, create, and delete instances at runtime.

* [  wrangler.jsonc ](#tab-panel-7192)
* [  wrangler.toml ](#tab-panel-7193)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "my-namespace"
    }
  ]
}
```

**TOML**

```toml
[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "my-namespace"
```

At runtime, `env.AI_SEARCH` is the namespace handle. Use `env.AI_SEARCH.get("my-instance")` to get a handle to a specific instance:

**TypeScript**

```ts
const instance = env.AI_SEARCH.get("my-instance");
const results = await instance.search({
  messages: [{ role: "user", content: "How does caching work?" }],
});
```

The `get()` method is synchronous and does not make a network call. The instance is resolved lazily when you call a method like `search()` or `chatCompletions()`.

## Default namespace

A `default` namespace is automatically created for every account. If you do not need multiple namespaces, use `default` for all your instances.

You can also bind directly to specific instances in the default namespace using the `ai_search` binding. This binds each entry to a single pre-existing instance without needing to call `get()`.

* [  wrangler.jsonc ](#tab-panel-7194)
* [  wrangler.toml ](#tab-panel-7195)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search": [
    {
      "binding": "PROD_SEARCH",
      "instance_name": "production"
    },
    {
      "binding": "STAGING_SEARCH",
      "instance_name": "staging"
    }
  ]
}
```

**TOML**

```toml
[[ai_search]]
binding = "PROD_SEARCH"
instance_name = "production"


[[ai_search]]
binding = "STAGING_SEARCH"
instance_name = "staging"
```

The `ai_search` binding provides the same instance methods (`search()`, `chatCompletions()`, `info()`, `stats()`, `items`) but does not support namespace-level operations like `list()`, `create()`, or `delete()`.

## Multiple namespaces

You can declare multiple namespace bindings in the same Worker. Each binding maps to a different namespace and provides isolated access to its instances.

* [  wrangler.jsonc ](#tab-panel-7196)
* [  wrangler.toml ](#tab-panel-7197)

**JSONC**

```jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "BLOG_SEARCH",
      "namespace": "blog"
    },
    {
      "binding": "SUPPORT_SEARCH",
      "namespace": "support"
    }
  ]
}
```

**TOML**

```toml
[[ai_search_namespaces]]
binding = "BLOG_SEARCH"
namespace = "blog"


[[ai_search_namespaces]]
binding = "SUPPORT_SEARCH"
namespace = "support"
```

## Namespaces and instance uniqueness

An instance name must be unique within a namespace. This means you can have an instance named `docs` in both the `blog` and `support` namespaces without conflict.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/concepts/namespaces/#page","headline":"Namespaces · Cloudflare AI Search docs","description":"Group AI Search instances into namespaces and manage them dynamically from a Workers binding.","url":"https://developers.cloudflare.com/ai-search/concepts/namespaces/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/concepts/namespaces/","name":"Namespaces"}}]}
```

---

---
title: Search modes
description: Compare AI Search vector, keyword, and hybrid search modes to choose the right retrieval strategy.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Search modes

AI Search supports three search modes: vector, keyword, and hybrid. By default, new instances use vector search only. You can enable keyword or hybrid search when creating or updating an instance.

## Vector search

Vector search converts your query into a vector embedding and finds chunks with similar meaning, even when the exact words differ. It knows that "deployment guide" and "how to ship my app" mean similar things. However, it can lose specifics. In a query like "ERR\_CONNECTION\_REFUSED timeout," vector search captures the broad concept of connection failures but might not surface the page that contains that exact error string.

## Keyword search

Keyword search matches chunks that contain your query terms exactly using BM25 full-text search. When you search "ERR\_CONNECTION\_REFUSED timeout," BM25 finds documents that actually contain "ERR\_CONNECTION\_REFUSED" as a term. However, it may miss a page about "troubleshooting network connections" that describes the same problem. Refer to [Keyword search](https://developers.cloudflare.com/ai-search/configuration/indexing/keyword-search/) for setup.

## Hybrid search

Hybrid search runs vector and keyword search in parallel and merges the results using a fusion method. Vector search understands intent, keyword search matches specific terms. Together, a query like "ERR\_CONNECTION\_REFUSED timeout" finds the exact error page and related troubleshooting content. Refer to [Hybrid search](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) for setup.

![Hybrid search](https://developers.cloudflare.com/_astro/hybrid-search.CJ9Cuw7h_13NTs.webp)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/concepts/search-modes/#page","headline":"Search modes · Cloudflare AI Search docs","description":"Compare AI Search vector, keyword, and hybrid search modes to choose the right retrieval strategy.","url":"https://developers.cloudflare.com/ai-search/concepts/search-modes/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/concepts/","name":"Concepts"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/concepts/search-modes/","name":"Search modes"}}]}
```

---

---
title: Limits &amp; pricing
description: View AI Search usage limits and pricing details for Free and Paid Workers plans.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Limits & pricing

AI Search usage limits and pricing depend on your [Workers plan](https://developers.cloudflare.com/workers/platform/pricing/).

## Limits

The following limits apply based on your [Workers plan](https://developers.cloudflare.com/workers/platform/pricing/):

| Limit                                       | Workers Free             | Workers Paid                 |
| ------------------------------------------- | ------------------------ | ---------------------------- |
| AI Search instances per account             | 100                      | 5,000                        |
| Namespaces per account                      | 100                      | 100                          |
| Files per instance                          | 100,000                  | 1M or 500K for hybrid search |
| Max file size                               | 4 MB                     | 4 MB                         |
| Queries per month                           | 20,000                   | Unlimited                    |
| Instances per cross-instance search request | 10                       | 10                           |
| Maximum pages crawled per day               | 500                      | Unlimited                    |
| Max custom metadata fields                  | 5 per AI Search instance | 5 per AI Search instance     |
| Max text metadata value length              | 500 characters           | 500 characters               |

Need a higher limit?

To request an adjustment to a limit, complete the [Limit Increase Request Form ↗](https://forms.gle/wnizxrEUW33Y15CT8). If the limit can be increased, Cloudflare will contact you with next steps.

## Pricing

During the open beta, AI Search is free within these limits. [Workers AI](https://developers.cloudflare.com/workers-ai/platform/pricing/) and [AI Gateway](https://developers.cloudflare.com/ai-gateway/reference/pricing/) usage is billed separately. Pricing details will be communicated at least 30 days before any billing begins.

## Historical billing

Instances created before AI Search moved to managed infrastructure ran on Cloudflare services in your own account, so older invoices may include separate charges for [R2](https://developers.cloudflare.com/r2/pricing/), [Vectorize](https://developers.cloudflare.com/vectorize/platform/pricing/), [Workers AI](https://developers.cloudflare.com/workers-ai/platform/pricing/), [AI Gateway](https://developers.cloudflare.com/ai-gateway/reference/pricing/), and [Browser Run](https://developers.cloudflare.com/browser-run/pricing/).

After the move, storage, vector indexing, and Browser Run usage for crawling are included. Workers AI and AI Gateway are still billed separately.

If your instance crawled a website, those pages now live in built-in storage. The dedicated R2 bucket AI Search originally created in your account is no longer used. It remains in your account, and any objects left in it may still count toward [R2 storage usage](https://developers.cloudflare.com/r2/pricing/). AI Search no longer writes to this bucket, so you can delete it if you no longer need its contents.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/platform/limits-pricing/#page","headline":"Limits & pricing · Cloudflare AI Search docs","description":"View AI Search usage limits and pricing details for Free and Paid Workers plans.","url":"https://developers.cloudflare.com/ai-search/platform/limits-pricing/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/platform/","name":"Platform"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/platform/limits-pricing/","name":"Limits & pricing"}}]}
```

---

---
title: Release note
description: Review recent changes to Cloudflare AI Search.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Release note

This release notes section covers regular updates and minor fixes. For major feature releases or significant updates, see the [changelog](https://developers.cloudflare.com/changelog).

## 2026-07-08

**GIF and BMP image support**

AI Search now indexes `.gif` and `.bmp` image files. They are converted to searchable text with Workers AI [Markdown conversion](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/) during sync, with no configuration changes. Refer to [supported file types](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types) for the full list.

## 2026-07-08

**Filter list items by exact object key**

The [list items endpoint](https://developers.cloudflare.com/ai-search/api/items/rest-api/) now accepts a `key` query parameter to return the item matching an exact object key. Keys are unique per data source, so combine `key` with `source` to disambiguate when the same key exists across multiple sources.

## 2026-06-24

**Configurable similarity cache duration and on-demand purge**

You can now control how long AI Search retains cached responses with the `cache_ttl` setting, from 10 minutes up to 6 days. The default is now 48 hours, from the previous fixed duration of 30 days. You can also purge all cached responses for an instance on demand. Refer to [similarity cache](https://developers.cloudflare.com/ai-search/configuration/retrieval/cache/).

## 2026-06-24

**Additional file type support**

Built-in storage, R2, and website data sources now support `.mdoc`, `.sql`, and `.log.gz` files. Refer to [supported file types](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types) for the full list.

## 2026-06-18

**Migration to managed infrastructure complete**

All AI Search instances have finished migrating to managed infrastructure. Every instance now includes built-in storage, a built-in vector index, and built-in web crawling, so the [current limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits) apply across all instances. Storage, vector indexing, and Browser Run usage for crawling are now included; [Workers AI](https://developers.cloudflare.com/workers-ai/platform/pricing/) and [AI Gateway](https://developers.cloudflare.com/ai-gateway/reference/pricing/) are still billed separately. If your instance previously crawled a website, the dedicated R2 bucket AI Search created in your account is no longer used and can be deleted.

## 2026-06-10

**Manage AI Search namespaces with Wrangler CLI**

AI Search now supports namespace-level Wrangler commands to create, list, update, and delete [namespaces](https://developers.cloudflare.com/ai-search/concepts/namespaces/) from the command line. Instance-level commands also accept a `--namespace` flag to target instances within a specific namespace, and `--json` output is available for automation and agent workflows. Refer to [Wrangler commands](https://developers.cloudflare.com/ai-search/wrangler-commands/) for full usage details.

## 2026-05-12

**Automatic migration to managed infrastructure on June 3, 2026**

Instances created before April 16, 2026 will be automatically migrated to managed infrastructure on **June 3, 2026**. The migration may take up to three days with no service downtime expected. No action is required. After migration, instances will include built-in storage, a built-in vector index, and built-in web crawling, and [new limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits) will apply. For full migration details, refer to [Limits & pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#historical-billing).

## 2026-04-16

**Hybrid search**

AI Search now supports [hybrid search](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/), combining vector and BM25 keyword search in a single query. Configure the tokenizer, keyword match mode, and fusion method per instance. Refer to [Search modes](https://developers.cloudflare.com/ai-search/concepts/search-modes/) for an overview.

## 2026-04-16

**Relevance boosting**

Boost search results by metadata fields like timestamp or priority using [relevance boosting](https://developers.cloudflare.com/ai-search/configuration/retrieval/boosting/). Configure up to 3 boost fields per instance or per request.

## 2026-04-16

**Cross-instance search**

Search across multiple AI Search instances in a single call using [namespace-level search](https://developers.cloudflare.com/ai-search/api/search/workers-binding/#namespace-level). Results are merged and ranked, with each chunk identifying which instance it came from.

## 2026-04-16

**Built-in storage and vector index**

New AI Search instances come with [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/) and a [built-in vector index](https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/#built-in-vector-index). Upload files directly to an instance using the Items API or the dashboard without setting up external infrastructure.

## 2026-04-16

**New AI Search Workers bindings**

Two new [Workers bindings](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) for AI Search. The `ai_search_namespaces` binding gives access to all instances within a [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) and supports dynamic instance management at runtime. The `ai_search` binding binds directly to a single instance for simpler use cases.

## 2026-04-01

**Wrangler CLI support for AI Search**

Manage AI Search instances from the command line with the `wrangler ai-search` command namespace. Create, list, update, delete, search, and get usage statistics for instances without leaving your terminal. All commands support `--json` for structured output that scripts and AI agents can parse directly. Refer to [Wrangler commands](https://developers.cloudflare.com/ai-search/wrangler-commands/) for full usage details.

## 2026-03-23

**Custom metadata filtering**

Define up to 5 custom metadata fields per AI Search instance and filter search results by category, version, or any custom attribute. Attach metadata via R2 custom headers or HTML meta tags.

## 2026-03-23

**Public endpoint, UI snippets, and MCP support**

AI Search now supports [public endpoints](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/), [UI snippets](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/), and [MCP](https://developers.cloudflare.com/ai-search/api/search/mcp/), making it easy to add search to your website or connect AI agents.

## 2026-03-23

**New REST API endpoints**

AI Search introduces new [REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/) endpoints for search that use an OpenAI-compatible format. You can use the familiar `messages` array structure that works with existing OpenAI SDKs and tools. The previous AutoRAG API endpoints will continue working as expected. New features will only be added to the new API. See the [migration guide](https://developers.cloudflare.com/ai-search/api/migration/rest-api/) for instructions.

## 2026-02-09

**Crawler user agent renamed**

The AI Search crawler user agent has been renamed from `Cloudflare-AutoRAG` to `Cloudflare-AI-Search`. You can continue using the previous user agent name, `Cloudflare-AutoRAG`, in your `robots.txt`. The Bot Detection ID, `122933950` for WAF rules remains unchanged.

## 2026-02-09

**Specify a single sitemap for website crawling**

You can now specify a single sitemap URL in **Parser options** to limit which pages are crawled. By default, AI Search crawls all sitemaps listed in your `robots.txt` from top to bottom.

## 2026-02-09

**Sync individual files**

You can now trigger a sync for a specific file from the dashboard. Go to **Overview** \> **Indexed Items** and select the sync icon next to the file you want to reindex.

## 2026-01-22

**New file type support**

AI Search now supports EMACS Lisp (`.el`) files and the `.htm` extension for HTML documents.

## 2026-01-19

**Path filtering for website and R2 data sources**

You can now filter which paths to include or exclude from indexing for both website and R2 data sources.

## 2026-01-19

**Simplified API instance creation**

API instance creation is now simpler with optional token\_id and model fields.

## 2026-01-16

**Website crawler improvements**

Website instances now respect sitemap `<priority>` for indexing order and `<changefreq>` for re-crawl frequency. Added support for `.gz` compressed sitemaps and partial URLs in robots.txt and sitemaps.

## 2026-01-16

**Improved indexing performance**

We have improved indexing performance for all AI Search instances. Support for more and larger files is coming.

## 2025-12-10

**Query rewrite visibility in AI Gateway logs**

Fixed a bug where query rewrites were not visible in the AI Gateway logs.

## 2025-11-19

**Custom HTTP headers for website crawling**

AI Search now supports custom HTTP headers for website crawling, allowing you to index content behind authentication or access controls.

## 2025-10-28

**Reranking and API-based system prompts**

You can now enable reranking to reorder retrieved documents by semantic relevance and set system prompts directly in API requests for per-query control.

## 2025-09-25

**AI Search (formerly AutoRAG) now supports more models**

Connect your provider keys through AI Gateway to use models from OpenAI, Anthropic, and other providers for both embeddings and inference.

## 2025-09-23

**Support document file types in AutoRAG**

Our [conversion utility](https://developers.cloudflare.com/workers-ai/features/markdown-conversion/) can now convert `.docx` and `.odt` files to Markdown, making these files available to index inside your AutoRAG instance.

## 2025-09-19

**Metrics view for AI Search**

AI Search now includes a Metrics tab to track file indexing, search activity, and top retrievals.

## 2025-08-28

**Website data source and NLWeb integration**

AI Search now supports websites as a data source. Connect your domain to automatically crawl and index your site content with continuous re-crawling. Also includes NLWeb integration for conversational search with `/ask` and `/mcp` endpoints.

## 2025-08-20

**Increased maximum query results to 50**

The maximum number of results returned from a query has been increased from **20** to **50**. This allows you to surface more relevant matches in a single request.

## 2025-07-16

**Deleted files now removed from index on next sync**

When a file is deleted from your R2 bucket, its corresponding chunks are now automatically removed from the Vectorize index linked to your AI Search instance during the next sync.

## 2025-07-08

**Faster indexing and new Jobs view**

Indexing is now 3-5x faster. A new Jobs view lets you monitor indexing progress, view job status, and inspect real-time logs.

## 2025-07-08

**Reduced cooldown between syncs**

The cooldown period between sync jobs has been reduced to 3 minutes, allowing you to trigger syncs more frequently.

## 2025-06-19

**Filter search by file name**

You can now filter AI Search queries by file name using the `filename` attribute for more control over which files are searched.

## 2025-06-19

**Custom metadata in search responses**

AI Search now returns custom metadata in search responses. You can also add a `context` field to guide AI-generated answers.

## 2025-06-16

**Rich format file size limit increased to 4 MB**

You can now index rich format files (e.g., PDF) up to 4 MB in size, up from the previous 1 MB limit.

## 2025-06-12

**Index processing status displayed on dashboard**

The dashboard now includes a new “Processing” step for the indexing pipeline that displays the files currently being processed.

## 2025-06-12

**Sync AI Search REST API published**

You can now trigger a sync job for an AI Search using the [Sync REST API](https://developers.cloudflare.com/api/resources/ai-search/subresources/rags/methods/sync/). This scans your data source for changes and queues updated or previously errored files for indexing.

## 2025-06-10

**Files modified in the data source will now be updated**

Files modified in your source R2 bucket will now be updated in the AI Search index during the next sync. For example, if you upload a new version of an existing file, the changes will be reflected in the index after the subsequent sync job. Please note that deleted files are not yet removed from the index. We are actively working on this functionality.

## 2025-05-31

**Errored files will now be retried in next sync**

Files that failed to index will now be automatically retried in the next indexing job. For instance, if a file initially failed because it was oversized but was then corrected (e.g. replaced with a file of the same name/key within the size limit), it will be re-attempted during the next scheduled sync.

## 2025-05-31

**Fixed character cutoff in recursive chunking**

Resolved an issue where certain characters (e.g. '#') were being cut off during the recursive chunking and embedding process. This fix ensures complete character processing in the indexing process.

## 2025-05-25

**EU jurisdiction R2 buckets now supported**

AI Search now supports R2 buckets configured with European Union (EU) jurisdiction restrictions. Previously, files in EU-restricted R2 buckets would not index when linked. This issue has been resolved, and all EU-restricted R2 buckets should now function as expected.

## 2025-04-23

**Metadata filtering and multitenancy support**

Filter search results by `folder` and `timestamp` to enable multitenancy and control the scope of retrieved results.

## 2025-04-23

**Response streaming in AI Search binding added**

AI Search now supports response streaming in the `AI Search` method of the [Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/), allowing you to stream results as they're retrieved by setting `stream: true`.

## 2025-04-07

**AI Search is now in open beta!**

AI Search allows developers to create fully-managed retrieval-augmented generation (RAG) pipelines powered by Cloudflare allowing developers to integrate context-aware AI into their applications without managing infrastructure. Get started today on the [Cloudflare Dashboard](https://dash.cloudflare.com/?to=/:account/ai/autorag).

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/platform/release-note/#page","headline":"Release note · Cloudflare AI Search docs","description":"Review recent changes to Cloudflare AI Search.","url":"https://developers.cloudflare.com/ai-search/platform/release-note/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/platform/","name":"Platform"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/platform/release-note/","name":"Release note"}}]}
```

---

---
title: API error codes
description: Troubleshoot API and public endpoint errors.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# API error codes

When a request to the AI Search API or a public endpoint fails, it returns one of the errors documented on this page.

Errors that occur while an item is indexing are handled separately. See [Indexing error codes](https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/).

## How errors are returned

The REST API and public endpoints return errors in a JSON envelope:

```json
{
  "success": false,
  "errors": [
    {
      "code": 7002,
      "message": "ai_search_not_found"
    }
  ],
  "result": {}
}
```

The [Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) throws exceptions. The exception `message` contains the AI Search error message, such as `ai_search_not_found`.

For Workers binding calls, the thrown error class depends on the upstream HTTP status:

| HTTP status | Workers binding error |
| ----------- | --------------------- |
| 404         | AiSearchNotFoundError |
| 5xx         | AiSearchInternalError |
| Other       | AiSearchError         |

## Common errors

These errors can occur across most AI Search API paths.

| Code  | Message                             | HTTP status | Details                                             | Recommended action                                                                                                                                                                                      |
| ----- | ----------------------------------- | ----------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 10000 | Authentication error                | 401         | Authentication failed.                              | Check your [API token](https://developers.cloudflare.com/ai-search/get-started/api/#1-create-an-api-token) and AI Search permissions.                                                                   |
| 7001  | Internal Error                      | 500         | An internal error occurred.                         | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| 7002  | ai\_search\_not\_found              | 404         | The requested instance does not exist.              | Check the instance name and namespace.                                                                                                                                                                  |
| 7017  | unable\_to\_connect\_to\_ai\_search | 503         | AI Search could not connect to an internal service. | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| 7063  | namespace\_not\_found               | 404         | The requested namespace does not exist.             | Check the [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) name.                                                                                                           |
| 7068  | Internal Error                      | 500         | An internal invariant failed.                       | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |

## Instances

These errors can occur when you create, read, update, delete, or get stats for AI Search instances through the REST API or Workers binding.

| Code | Message                                                         | HTTP status | Details                                                                             | Recommended action                                                                                                                                                                                                                              |
| ---- | --------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 7002 | ai\_search\_not\_found                                          | 404         | The requested instance does not exist.                                              | Check the instance name and namespace.                                                                                                                                                                                                          |
| 7017 | unable\_to\_connect\_to\_ai\_search                             | 503         | AI Search could not connect to the indexing engine.                                 | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                         |
| 7010 | invalid\_model                                                  | 400         | The configured or requested model is invalid.                                       | Use a [supported model](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).                                                                                                                                    |
| 7018 | ai\_gateway\_not\_found                                         | 400         | The AI Gateway configured for the instance was not found.                           | Set ai\_gateway\_id to an existing gateway when you [create or update the instance](https://developers.cloudflare.com/ai-search/api/instances/rest-api/), or create the gateway in [AI Gateway](https://developers.cloudflare.com/ai-gateway/). |
| 7012 | ai\_search\_instance\_invalid\_token                            | 400         | The service API token configured for the instance is invalid.                       | Create or update the [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) used by the instance.                                                                                           |
| 7013 | max\_instances\_reached                                         | 403         | The account reached its instance limit.                                             | Delete unused instances or [request a higher limit](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits).                                                                                                               |
| 7022 | ai\_search\_with\_this\_name\_already\_exist                    | 400         | An instance with this name already exists in the namespace.                         | Use a different instance name or [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/).                                                                                                                                 |
| 7023 | domain\_not\_owned\_by\_user                                    | 400         | AI Search could not confirm ownership of a website data source domain.              | Check that the domain is [onboarded to Cloudflare](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/).                                                                                                                    |
| 7024 | invalid\_domain                                                 | 400         | The website data source domain is invalid.                                          | Check the [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) URL.                                                                                                                            |
| 7028 | missing\_sitemap                                                | 400         | AI Search could not find a valid sitemap for the website data source.               | Add or update the website [sitemap](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#sitemap).                                                                                                                    |
| 7029 | missing\_robots\_txt                                            | 400         | AI Search could not fetch robots.txt for the website data source.                   | Add a valid [robots.txt](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#robotstxt) file with sitemap information.                                                                                               |
| 7034 | forbidden\_robots\_txt                                          | 400         | robots.txt blocks AI Search from crawling the website data source.                  | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#robotstxt) to crawl the site.                                                                                                      |
| 7035 | forbidden\_sitemap                                              | 400         | AI Search cannot access the website data source sitemap.                            | Allow the AI Search crawler to access the [sitemap URL](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#specific-sitemap).                                                                                       |
| 7036 | invalid\_chunk\_size                                            | 400         | The chunk size exceeds the embedding model input token limit.                       | Use a smaller [chunk size](https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/) based on the [embedding model limit](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/#embedding).    |
| 7040 | invalid\_custom\_header                                         | 400         | A website data source crawl header is invalid or not allowed.                       | Review [extra headers](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#extra-headers) and remove unsupported headers.                                                                                            |
| 7045 | specific\_sitemaps\_only\_valid\_when\_parse\_type\_is\_sitemap | 400         | Specific sitemaps were provided for an incompatible website data source parse type. | Use [specific sitemaps](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#specific-sitemap) only with sitemap parsing.                                                                                             |
| 7047 | invalid\_url\_location                                          | 400         | A website data source URL location is invalid.                                      | Check the [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) URL.                                                                                                                            |
| 7050 | fail\_while\_provisioning\_managed\_resources                   | 500         | AI Search could not create managed resources for an instance.                       | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if provisioning continues to fail.                             |
| 7052 | type\_and\_source\_are\_required\_for\_non\_managed\_instances  | 400         | A non-managed instance is missing a type or source.                                 | Provide the required [data source](https://developers.cloudflare.com/ai-search/configuration/data-source/) fields.                                                                                                                              |

## Namespaces

These errors can occur when you create, list, read, update, or delete namespaces, or move instances between namespaces.

| Code | Message                                      | HTTP status | Details                                                                                                 | Recommended action                                                                                                                 |
| ---- | -------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| 7022 | ai\_search\_with\_this\_name\_already\_exist | 400         | An instance with this name already exists in the target namespace.                                      | Use a different instance name or [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/).                    |
| 7062 | max\_namespaces\_reached                     | 403         | The account reached the limit of 100 namespaces.                                                        | Delete unused namespaces or [request a higher limit](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits). |
| 7063 | namespace\_not\_found                        | 404         | The requested namespace does not exist.                                                                 | Check the [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) name.                                      |
| 7064 | namespace\_already\_exists                   | 409         | The namespace already exists.                                                                           | Use a different namespace name or update the existing namespace.                                                                   |
| 7065 | cannot\_modify\_default\_namespace           | 400         | The default namespace is created for every account and cannot be deleted or modified by this operation. | Use a non-default [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/) for this operation.                |
| 7066 | namespace\_not\_empty                        | 400         | The namespace still contains instances.                                                                 | Move or delete instances before deleting the [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/).        |
| 7067 | namespace\_same\_name                        | 400         | The source and target namespace names are the same.                                                     | Choose a different target [namespace](https://developers.cloudflare.com/ai-search/concepts/namespaces/).                           |

## Tokens

These errors can occur when you create, list, read, update, or delete service API tokens for AI Search.

| Code | Message                              | HTTP status | Details                                    | Recommended action                                                                                                                                               |
| ---- | ------------------------------------ | ----------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 7012 | ai\_search\_instance\_invalid\_token | 400         | The token is invalid.                      | Create or update the [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/).                                 |
| 7075 | token\_not\_found                    | 404         | The requested token does not exist.        | Create a new [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/).                                         |
| 7076 | token\_in\_use\_by\_instances        | 409         | One or more instances still use the token. | Update or delete those instances before deleting the [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/). |

## Items

These errors can occur when you upload, list, read, download, delete, sync, filter, or inspect indexed items through the Items API or Workers binding.

| Code | Message                                                         | HTTP status | Details                                                                    | Recommended action                                                                                                                                    |
| ---- | --------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| 7032 | ai\_search\_is\_paused                                          | 400         | The instance is paused.                                                    | Resume the instance before uploading items.                                                                                                           |
| 7041 | item\_not\_found                                                | 404         | The requested item does not exist.                                         | Check the item ID.                                                                                                                                    |
| 7042 | item\_key\_already\_exist                                       | 409         | An item with this key already exists.                                      | Use a different filename or manage the existing item with the [Items API](https://developers.cloudflare.com/ai-search/api/items/workers-binding/).    |
| 7044 | unable\_to\_sync\_item                                          | 503         | AI Search could not sync the item.                                         | Retry the operation. Check [indexing error codes](https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/) for details.     |
| 7053 | this\_operation\_requires\_a\_managed\_instance                 | 400         | The operation only works on managed instances.                             | Use an instance with [built-in storage](https://developers.cloudflare.com/ai-search/configuration/data-source/built-in-storage/).                     |
| 7054 | file\_exceeds\_maximum\_size                                    | 413         | The uploaded file is too large.                                            | Reduce the file size before uploading. Review [file size limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits).        |
| 7055 | file\_field\_is\_required                                       | 400         | The upload request is missing the file field.                              | Include a file field in the multipart form data.                                                                                                      |
| 7056 | invalid\_metadata\_format                                       | 400         | The upload metadata is not valid.                                          | Send upload metadata as a valid JSON object. See [metadata attributes](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/). |
| 7058 | invalid\_metadata\_filter                                       | 400         | The metadata filter is not valid.                                          | Check the [filter syntax](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/#filter-syntax) and field names.              |
| 7059 | content\_download\_not\_available\_for\_external\_source\_items | 400         | The original content is not available for an item from an external source. | Download the file from the original [data source](https://developers.cloudflare.com/ai-search/configuration/data-source/).                            |
| 7060 | unsupported\_file\_type                                         | 400         | AI Search could not determine a supported content type.                    | Upload a [supported file type](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types).                          |
| 7072 | filename\_exceeds\_maximum\_length                              | 400         | The filename or item key is longer than 128 characters.                    | Use a filename or item key that is 128 characters or fewer.                                                                                           |

## Jobs

These errors can occur when you create, list, read, cancel, or list logs for sync jobs through the REST API or Workers binding.

| Code | Message                    | HTTP status | Details                                                                           | Recommended action                                         |
| ---- | -------------------------- | ----------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| 7020 | sync\_in\_cooldown         | 429         | A user-triggered sync job was requested within 30 seconds of a previous sync job. | Wait at least 30 seconds before starting another sync job. |
| 7021 | job\_not\_found            | 404         | The requested job does not exist.                                                 | Check the job ID.                                          |
| 7046 | job\_cannot\_be\_cancelled | 400         | The job has already ended and cannot be cancelled.                                | Refresh the job status before cancelling.                  |

## Search and chat

### Search

These errors can occur when you run instance search, cross-instance search, public endpoint search, or `instance.search()` through the REST API, Workers binding, or public endpoints.

| Code | Message                                                   | HTTP status | Details                                                                                                                                    | Recommended action                                                                                                                                                                                                                |
| ---- | --------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 7010 | invalid\_model                                            | 400         | The configured or requested model is invalid.                                                                                              | Use a [supported model](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).                                                                                                                      |
| 7015 | filter\_or\_operator\_only\_supports\_eq\_filters         | 400         | The or filter contains unsupported operators.                                                                                              | Use supported [filter syntax](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/#filter-syntax).                                                                                                      |
| 7016 | filter\_or\_operator\_does\_not\_support\_different\_keys | 400         | The or filter includes multiple metadata keys.                                                                                             | Use the same [metadata attribute](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/) for every comparison inside the or filter.                                                                        |
| 7039 | missing\_user\_query                                      | 400         | A search request does not include a user query.                                                                                            | Include query or a user message in the [messages format](https://developers.cloudflare.com/ai-search/api/search/rest-api/#search-and-chat).                                                                                       |
| 7057 | invalid\_datetime\_filter\_value                          | 400         | A datetime metadata filter value is invalid.                                                                                               | Use a valid datetime value in your [metadata filter](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/).                                                                                             |
| 7058 | invalid\_metadata\_filter                                 | 400         | The metadata filter is not valid.                                                                                                          | Check the [filter syntax](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/#filter-syntax) and field names.                                                                                          |
| 7069 | monthly\_query\_quota\_exceeded                           | 429         | The account reached the monthly query quota for its Workers plan.                                                                          | Review [AI Search limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits), wait for the quota to reset, or upgrade your [Workers plan](https://developers.cloudflare.com/workers/platform/pricing/). |
| 7070 | invalid\_retrieval\_type                                  | 400         | The request set retrieval\_type to a mode the instance's index\_method does not support. Both keyword and hybrid require keyword indexing. | Enable the required [index method](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) on the instance, which triggers a reindex. If the override was unintentional, remove retrieval\_type.       |
| 7071 | vectorize\_authentication\_failed                         | 401         | AI Search could not authenticate with Vectorize.                                                                                           | Check the instance configuration and [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/).                                                                                  |
| 7073 | all\_search\_methods\_failed                              | 500         | All retrieval methods failed.                                                                                                              | Retry the request. Check [indexing error codes](https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/) and instance configuration.                                                                    |
| 7080 | vectorize\_filter\_not\_serializable                      | 400         | A filter cannot be sent to Vectorize.                                                                                                      | Use JSON-serializable filter values.                                                                                                                                                                                              |
| 7089 | image\_query\_requires\_vector\_index                     | 400         | An image query requires vector indexing.                                                                                                   | Turn on [vector search](https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/) for the instance and reindex your content, or use an instance that already has vector search enabled.                  |

### Chat

These errors can occur when AI Search retrieves context and generates a response through instance chat completions, cross-instance chat completions, public endpoint chat completions, or `instance.chatCompletions()`.

| Code | Message                               | HTTP status | Details                                                                                                                                    | Recommended action                                                                                                                                                                                                                |
| ---- | ------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 7010 | invalid\_model                        | 400         | The configured or requested model is invalid.                                                                                              | Use a [supported model](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).                                                                                                                      |
| 7038 | missing\_user\_query                  | 400         | A chat completions request does not include a user query.                                                                                  | Include at least one user message in the [messages format](https://developers.cloudflare.com/ai-search/api/search/rest-api/#search-and-chat).                                                                                     |
| 7069 | monthly\_query\_quota\_exceeded       | 429         | The account reached the monthly query quota for its Workers plan.                                                                          | Review [AI Search limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits), wait for the quota to reset, or upgrade your [Workers plan](https://developers.cloudflare.com/workers/platform/pricing/). |
| 7070 | invalid\_retrieval\_type              | 400         | The request set retrieval\_type to a mode the instance's index\_method does not support. Both keyword and hybrid require keyword indexing. | Enable the required [index method](https://developers.cloudflare.com/ai-search/configuration/indexing/hybrid-search/) on the instance, which triggers a reindex. If the override was unintentional, remove retrieval\_type.       |
| 7073 | all\_search\_methods\_failed          | 500         | All retrieval methods failed.                                                                                                              | Retry the request. Check [indexing error codes](https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/) and instance configuration.                                                                    |
| 7089 | image\_query\_requires\_vector\_index | 400         | An image query requires vector indexing.                                                                                                   | Turn on [vector search](https://developers.cloudflare.com/ai-search/configuration/indexing/vector-search/) for the instance and reindex your content, or use an instance that already has vector search enabled.                  |

### Cross-instance search and chat

These errors can occur when you use [cross-instance search or chat](https://developers.cloudflare.com/ai-search/api/search/rest-api/#cross-instance-search-and-chat) to query multiple instances in one request.

| Code | Message                                   | HTTP status | Details                                                             | Recommended action                                                                                                                     |
| ---- | ----------------------------------------- | ----------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| 7049 | one\_or\_more\_instance\_searches\_failed | 500         | A cross-instance search failed and return\_on\_failure is disabled. | Retry the request, or allow partial results with return\_on\_failure.                                                                  |
| 7074 | too\_many\_multi\_search\_instances       | 400         | A cross-instance search includes too many instances.                | Reduce instance\_ids to 10 or fewer, the [allowed limit](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits). |

When `return_on_failure` is enabled, cross-instance search can return partial results with `errors: [{ instance_id, message: "search_failed" }]`. That response does not use a numeric error code.

### Models and AI Gateway

These errors can occur when a search or chat request calls Workers AI, AI Gateway, or an external model provider.

| Code | Message                                           | HTTP status | Details                                                      | Recommended action                                                                                                                                                                                      |
| ---- | ------------------------------------------------- | ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 2003 | Rate limited                                      | 429         | AI Gateway rate limited the request.                         | Retry with backoff and review [public endpoint rate limits](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/#rate-limiting), if applicable.                         |
| 2016 | Prompt blocked due to security configurations     | 424         | AI Gateway Guardrails blocked the prompt.                    | Review [AI Gateway Guardrails](https://developers.cloudflare.com/ai-gateway/features/guardrails/set-up-guardrail/) prompt settings and the prompt content.                                              |
| 2017 | Response blocked due to security configurations   | 424         | AI Gateway Guardrails blocked the response.                  | Review [AI Gateway Guardrails](https://developers.cloudflare.com/ai-gateway/features/guardrails/set-up-guardrail/) response settings and the retrieved content.                                         |
| 7011 | workers\_ai\_fail\_to\_return\_a\_valid\_response | 500         | Workers AI returned an invalid response.                     | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| 7019 | workers\_ai\_error                                | 400         | Workers AI returned an error for the request.                | Check the [model](https://developers.cloudflare.com/ai-search/configuration/models/), input, and AI Search options.                                                                                     |
| 7030 | workers\_ai\_timeout                              | 400         | Workers AI timed out.                                        | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| 7031 | ai\_gateway\_timeout                              | 400         | AI Gateway timed out.                                        | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| 7033 | ai\_gateway\_exception                            | 502         | AI Gateway or the upstream model returned an error.          | Retry the request. Check [AI Gateway](https://developers.cloudflare.com/ai-gateway/) and provider configuration.                                                                                        |
| 7077 | ai\_gateway\_authentication\_error                | 401         | AI Gateway or the upstream provider rejected authentication. | Check provider credentials in [AI Gateway](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/).                                                                            |
| 7078 | ai\_gateway\_billing\_error                       | 402         | The upstream provider reported a billing issue.              | Check provider billing status in [AI Gateway](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/).                                                                         |
| 7079 | ai\_gateway\_context\_window\_exceeded            | 413         | The request exceeds the model context window.                | Reduce message history, retrieved context, or [result count](https://developers.cloudflare.com/ai-search/configuration/retrieval/result-controls/#maximum-number-of-results).                           |

## Public endpoints

These errors can occur when public search, public chat completions, Model Context Protocol (MCP), snippet analytics, assets, or public endpoint routing fails before the request is proxied to AI Search. Public endpoint `/search` and `/chat/completions` can also return the AI Search API errors listed above.

| Code  | Message                                                        | HTTP status | Details                                                                                             | Recommended action                                                                                                                                                                                                     |
| ----- | -------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 60001 | asset not found                                                | 404         | The requested UI snippet asset path does not exist, such as an incorrect or outdated asset version. | Use the <script> tag from the [UI snippet library](https://developers.cloudflare.com/ai-search/configuration/retrieval/embed-search-snippets/) without changes, and update the asset version if it is outdated.        |
| 60002 | hash not found on url                                          | 404         | The public endpoint hash is missing from the URL.                                                   | Use the [public endpoint URL](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/#public-url-format) copied from the dashboard.                                                       |
| 60003 | config not found                                               | 404         | The public endpoint configuration was not found.                                                    | Confirm that the [public endpoint](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/) is enabled.                                                                                   |
| 60004 | ai search not enabled                                          | 404         | The public endpoint is disabled.                                                                    | Enable the [public endpoint](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/#enabling-and-disabling-public-endpoints) for the instance.                                           |
| 60005 | rate limited                                                   | 429         | The public endpoint rate limit was exceeded.                                                        | Retry after the [rate limit](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/#rate-limiting) resets.                                                                               |
| 60006 | endpoint not found                                             | 404         | The requested public endpoint route does not exist.                                                 | Use a supported [public endpoint](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/#available-endpoints).                                                                           |
| 60007 | mcp endpoint disabled                                          | 401         | The MCP endpoint is disabled.                                                                       | Enable MCP for the [public endpoint](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/).                                                                                            |
| 60008 | search endpoint disabled                                       | 401         | The search endpoint is disabled.                                                                    | Enable the search endpoint in [public endpoint settings](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/).                                                                        |
| 60009 | chat completions endpoint disabled                             | 401         | The chat completions endpoint is disabled.                                                          | Enable the chat completions endpoint in [public endpoint settings](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/).                                                              |
| 60010 | method not allowed                                             | 405         | The public endpoint snippet analytics /stats endpoint received an unsupported HTTP method.          | Send snippet analytics requests to /stats with POST.                                                                                                                                                                   |
| 60011 | invalid stats request body                                     | 400         | The public endpoint snippet analytics /stats request body is invalid.                               | Send a valid JSON body with a non-empty events array.                                                                                                                                                                  |
| 60012 | invalid instance\_ids query parameter                          | 400         | A namespace public endpoint received a malformed instance\_ids query parameter.                     | Use a comma-separated instance\_ids value, or omit it to search the configured namespace endpoint instances.                                                                                                           |
| 60013 | instance\_ids contains values outside the configured allowlist | 400         | A namespace public endpoint request includes instance IDs outside its allowlist.                    | Use only instance IDs configured for the namespace public endpoint.                                                                                                                                                    |
| 60014 | path not supported for namespace-kind hash                     | 404         | A namespace public endpoint received an unsupported path.                                           | Use /search, /chat/completions, or /mcp.                                                                                                                                                                               |
| 60015 | request body must be a JSON object                             | 400         | The public endpoint request body is not a JSON object.                                              | Send the request body as a JSON object with Content-Type: application/json, not an array, string, or empty body. See [public endpoint usage](https://developers.cloudflare.com/ai-search/api/search/public-endpoint/). |
| 60016 | method not allowed; this MCP endpoint only accepts POST        | 405         | The MCP endpoint received a non-POST request.                                                       | Send [MCP](https://developers.cloudflare.com/ai-search/api/search/mcp/) requests with POST.                                                                                                                            |
| 60100 | internal error                                                 | 500         | The public endpoint returned an unexpected error.                                                   | Retry the request. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                |

## Troubleshoot API errors

If an API request fails, check the `code` and `message` fields in the error response. For Workers binding calls, check the thrown error `name` and `message`.

For transient service errors, retry with exponential backoff. If an internal or service error persists, [contact Cloudflare support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) with the error code, instance ID, and request timestamp.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/troubleshooting/api-error-codes/#page","headline":"API error codes · Cloudflare AI Search docs","description":"Troubleshoot API and public endpoint errors.","url":"https://developers.cloudflare.com/ai-search/troubleshooting/api-error-codes/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/troubleshooting/","name":"Troubleshooting"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/troubleshooting/api-error-codes/","name":"API error codes"}}]}
```

---

---
title: Indexing error codes
description: Resolve asynchronous indexing, sync, and crawl error codes.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/ai-search/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Indexing error codes

After AI Search accepts an upload, sync, or crawl request, it processes your content in the background so it can be searched. When processing fails, AI Search records one of the error codes on this page. Some errors affect a single item; others pause the whole instance.

Because this processing happens after the request succeeds, the errors are not returned in the original API response. To find them, check [item logs](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/logs/), item details, or instance stats. Errors returned immediately when a request fails are documented separately in [API error codes](https://developers.cloudflare.com/ai-search/troubleshooting/api-error-codes/).

## How errors are returned

Indexing errors are asynchronous: an upload, sync, or crawl request can succeed, then the content can fail later while AI Search processes it. They fall into two categories:

* **[Item-level errors](#item-level-errors)** affect a single item. The item moves to `status: "error"` while the rest of the instance keeps indexing.
* **[Instance-level errors](#instance-level-errors)** affect the whole instance. AI Search pauses indexing because the problem, such as a source, token, model, or limit issue, blocks every item.

To retry a single failed item, check [item logs](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/logs/), fix any clear source or configuration issue, then [sync the item](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/sync/) again. For website and R2 data sources, you can also run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/).

If the instance is paused, resolve the underlying cause, then resume the instance.

If a transient error persists after retrying, [contact Cloudflare support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) with the item ID, instance ID, error code, and request timestamp.

## Item-level errors

These errors affect a single item. The item moves to `status: "error"` while the rest of the instance keeps indexing. Fix the item or retry it.

### File and content

These errors appear when AI Search cannot read, convert, chunk, or embed a source file. For supported formats and file size limits, refer to [Data source](https://developers.cloudflare.com/ai-search/configuration/data-source/). For chunking and model limits, refer to [Chunking](https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/) and [Supported models](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/).

| Error                                  | Details                                                                                        | Recommended action                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| -------------------------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| unknown\_error                         | An unexpected processing error occurred.                                                       | Check [item logs](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/logs/) for the failed step, then [sync the item](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/sync/) again. If the error persists, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/). |
| over\_size                             | The file exceeds the maximum allowed size.                                                     | Reduce the file size, split the file, or exclude it. Review [file limits](https://developers.cloudflare.com/ai-search/configuration/data-source/#file-limits).                                                                                                                                                                                                                                                                                                                   |
| unsupported\_type                      | The file type is not supported.                                                                | Convert the file to a [supported file type](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types), then upload or sync it again.                                                                                                                                                                                                                                                                                                          |
| file\_not\_found                       | The file was not found in the source.                                                          | Restore the source file, then [sync the item](https://developers.cloudflare.com/api/resources/ai%5Fsearch/subresources/namespaces/subresources/instances/subresources/items/methods/sync/) or run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/). If the file was intentionally deleted, run a source sync so AI Search can update the index.                                                                                  |
| invalid\_url                           | The file URL is invalid.                                                                       | Fix the URL in the [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) or sitemap, then run a source sync job.                                                                                                                                                                                                                                                                                                                 |
| file\_is\_corrupt                      | The file is corrupt.                                                                           | Replace the file with an uncorrupted copy, then upload or sync it again.                                                                                                                                                                                                                                                                                                                                                                                                         |
| file\_is\_password\_locked             | The file is encrypted or requires a password before AI Search can read its contents.           | Remove the password, upload an unlocked copy, then sync the item again.                                                                                                                                                                                                                                                                                                                                                                                                          |
| invalid\_pdf                           | AI Search could not parse the file as a valid PDF.                                             | Upload a PDF that opens correctly, or convert the content to another [supported file type](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types).                                                                                                                                                                                                                                                                                         |
| unable\_to\_convert\_to\_markdown      | AI Search could not convert the file to text.                                                  | Use a [supported file type](https://developers.cloudflare.com/ai-search/configuration/data-source/#supported-file-types) or replace scanned content with extractable text.                                                                                                                                                                                                                                                                                                       |
| markdown\_too\_large                   | AI Search converted the file, but the generated Markdown exceeded AI Search processing limits. | If the source file is within [AI Search file limits](https://developers.cloudflare.com/ai-search/configuration/data-source/#file-limits), [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/). As a workaround, split the source into smaller files if practical.                                                                                                                                                                        |
| markdown\_conversion\_empty            | AI Search converted the file, but the conversion returned no usable text.                      | Ensure the file contains extractable text. Scanned or image-only files produce no text, so add a text layer or upload a text-based version.                                                                                                                                                                                                                                                                                                                                      |
| file\_content\_empty                   | The file is empty or contains only headings.                                                   | Add searchable body content, then upload or sync the file again.                                                                                                                                                                                                                                                                                                                                                                                                                 |
| chunk\_too\_large\_for\_storage        | AI Search generated a chunk that exceeded internal storage limits.                             | Lower [chunk size](https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/) if you configured a high value. If the item still fails, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).                                                                                                                                                                                                                           |
| timeout\_error                         | Item processing timed out.                                                                     | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                                                                                                                                                                                                                        |
| file\_length\_exceed\_embedding\_model | The file is too long for the embedding model.                                                  | Use a smaller source file, or [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the file is within documented limits.                                                                                                                                                                                                                                                                                                               |
| file\_too\_large\_for\_embedder        | The file exceeds the maximum byte size accepted by the embedding model.                        | Use a smaller source file, or [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the file is within documented limits.                                                                                                                                                                                                                                                                                                               |

### Website crawl

These errors appear when AI Search cannot fetch, render, or include a page from a [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/). AI Search uses [Browser Run](https://developers.cloudflare.com/browser-run/) in the background to crawl and render pages and manages it for you.

| Error                                                  | Details                                                                                               | Recommended action                                                                                                                                                                                                                                                                                                                 |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| too\_many\_redirects                                   | The crawler followed too many redirects or found a redirect loop.                                     | Fix the redirect chain for the [website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/), then run a source sync job.                                                                                                                                                                  |
| invalid\_host\_after\_redirect                         | The page redirected to a hostname outside the current crawl scope.                                    | Check item logs for the redirect target. If that target should be indexed, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/). Otherwise, exclude the original URL with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/).             |
| subdomains\_not\_allowed                               | The URL is outside the hostnames AI Search can crawl for this website data source.                    | If this URL should be indexed by the instance, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/). Otherwise, exclude it with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/).                                                       |
| blocked\_by\_robots\_txt                               | robots.txt disallowed the crawl.                                                                      | Update robots.txt so the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#robotstxt) can access the site.                                                                                                                                                                        |
| blocked\_by\_robots\_txt\_path                         | robots.txt disallowed the path.                                                                       | Allow the path in [robots.txt](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#robotstxt), or exclude it with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/) if you do not want it indexed.                                                   |
| blocked\_by\_content\_signal                           | The site blocked the crawl with Content Signals.                                                      | Review Content Signals directives in robots.txt. If the content should not be indexed, exclude it with path filtering.                                                                                                                                                                                                             |
| excluded\_by\_path\_filter                             | Your path filter excluded the item.                                                                   | Review [include and exclude rules](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/#filtering-behavior) if the item should be indexed.                                                                                                                                                           |
| network\_connection\_lost                              | The network connection was interrupted.                                                               | Sync the item again. If the error persists, check that the source is reachable.                                                                                                                                                                                                                                                    |
| crawl\_got\_http\_error                                | The crawler received an HTTP error.                                                                   | Check item logs for the HTTP status. Fix the origin response or access controls, then run a source sync job.                                                                                                                                                                                                                       |
| crawl\_got\_http\_401                                  | The crawler received 401 Unauthorized.                                                                | Make the page accessible to the crawler. For protected pages, configure [extra headers](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#extra-headers) or Cloudflare Access service credentials.                                                                                                    |
| crawl\_got\_http\_403                                  | The crawler received 403 Forbidden.                                                                   | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#allow-the-ai-search-crawler-through-waf) through your access controls and origin firewall.                                                                                                                            |
| crawl\_got\_http\_429                                  | The origin rate limited the crawler.                                                                  | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#allow-the-ai-search-crawler-through-waf), raise the origin limit, or narrow crawl scope with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/).                    |
| blocked\_by\_payment                                   | The site returned 402 Payment Required.                                                               | Exclude the page with path filtering. AI Search does not support paid crawls.                                                                                                                                                                                                                                                      |
| blocked\_by\_waf                                       | A web application firewall (WAF) blocked the crawler.                                                 | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#allow-the-ai-search-crawler-through-waf) in your WAF.                                                                                                                                                                 |
| blocked\_by\_bot\_management                           | Bot controls blocked the crawler.                                                                     | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#allow-the-ai-search-crawler-through-waf) in your bot protection settings.                                                                                                                                             |
| blocked\_by\_turnstile                                 | Turnstile blocked the crawler.                                                                        | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#allow-the-ai-search-crawler-through-waf), or exclude the page with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/).                                              |
| http\_4xx                                              | The site returned an HTTP 4xx error.                                                                  | Check item logs for the exact status. Fix the URL or access controls, or exclude the page with path filtering.                                                                                                                                                                                                                     |
| http\_5xx                                              | The site returned an HTTP 5xx error.                                                                  | Fix origin health, then sync the item or run a source sync job.                                                                                                                                                                                                                                                                    |
| unreachable\_timeout                                   | The crawler could not reach the page before timeout.                                                  | Check origin latency, firewall rules, and page availability, then run a source sync job.                                                                                                                                                                                                                                           |
| unreachable\_dns                                       | The source domain did not resolve.                                                                    | Check [DNS records](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/) for the source domain.                                                                                                                                                                                                    |
| page\_limit\_reached                                   | The managed crawler reached your plan's daily page limit, so some pages were not crawled in this run. | Upgrade to Workers Paid for unlimited daily crawling, or reduce the pages in scope with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/). Free plans crawl up to 500 pages per day. See [limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits). |
| browser\_rendering\_unknown\_error                     | Browser Run returned an unknown error.                                                                | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the same page keeps failing.                          |
| browser\_rendering\_authentication\_error              | Browser Run required authentication.                                                                  | Configure [extra headers](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#extra-headers) for protected pages, or exclude the page.                                                                                                                                                                  |
| browser\_rendering\_no\_body\_status\_error            | Browser Run returned no page content for AI Search to index.                                          | If the page should be indexed, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/). Otherwise, exclude the page with [path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/).                                                                 |
| browser\_rendering\_timeout\_error                     | Browser Run timed out.                                                                                | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                   |
| browser\_rendering\_network\_connection\_closed\_error | The browser connection closed during rendering.                                                       | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                   |
| browser\_rendering\_server\_refused\_connection\_error | The origin refused the browser connection.                                                            | Allow the [AI Search crawler](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#allow-the-ai-search-crawler-through-waf) through your origin network controls, then run a source sync job.                                                                                                            |
| browser\_rendering\_rate\_limit\_error                 | Browser Run was rate limited.                                                                         | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) later. If the error persists, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).                                                                                                    |

### Models and AI Gateway

These errors appear when [embedding models](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/#embedding), [Workers AI](https://developers.cloudflare.com/workers-ai/), external providers, or [AI Gateway](https://developers.cloudflare.com/ai-gateway/) cannot process item content.

| Error                                     | Details                                                | Recommended action                                                                                                                                                                                                                                                                    |
| ----------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| error\_embedding\_data\_with\_workers\_ai | Workers AI could not generate embeddings.              | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                             |
| workers\_ai\_invalid\_input               | Workers AI rejected the input.                         | Check file content, file type, and [embedding model support](https://developers.cloudflare.com/ai-search/configuration/models/supported-models/#embedding).                                                                                                                           |
| workers\_ai\_free\_allocation\_exceeded   | Workers AI free tier allocation was exceeded.          | Review [Workers AI pricing](https://developers.cloudflare.com/workers-ai/platform/pricing/) and wait for allocation reset, or upgrade your Workers plan.                                                                                                                              |
| workers\_ai\_internal\_error              | Workers AI returned an internal error.                 | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                             |
| workers\_ai\_out\_of\_capacity\_error     | Workers AI capacity was unavailable.                   | Retry later and review [Workers AI limits](https://developers.cloudflare.com/workers-ai/platform/limits/).                                                                                                                                                                            |
| workers\_ai\_timeout\_error               | Workers AI timed out.                                  | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                             |
| error\_external\_model\_unknown\_error    | An external embedding model returned an unknown error. | Check provider status and [AI Gateway logs](https://developers.cloudflare.com/ai-gateway/observability/logging/), then sync the item again.                                                                                                                                           |
| error\_external\_model\_rate\_limited     | An external embedding model rate limited the request.  | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) later, or raise the rate limit with your model provider. Check [AI Gateway logs](https://developers.cloudflare.com/ai-gateway/observability/logging/) for the provider response. |
| error\_external\_model\_unauthorized      | An external embedding model rejected authentication.   | Check provider credentials in [AI Gateway](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/), then sync the item again.                                                                                                                                |
| ai\_gateway\_request\_blocked\_firewall   | AI Gateway Guardrails blocked the request.             | Review [AI Gateway Guardrails](https://developers.cloudflare.com/ai-gateway/features/guardrails/set-up-guardrail/) prompt settings for the configured gateway, then run a source sync job again.                                                                                      |
| ai\_gateway\_dlp\_blocked                 | AI Gateway Data Loss Prevention (DLP) blocked content. | Review [AI Gateway DLP settings](https://developers.cloudflare.com/ai-gateway/features/dlp/set-up-dlp/) and the item content.                                                                                                                                                         |
| ai\_gateway\_response\_blocked\_firewall  | AI Gateway Guardrails blocked the response.            | Review [AI Gateway Guardrails](https://developers.cloudflare.com/ai-gateway/features/guardrails/set-up-guardrail/) response settings for the configured gateway, then run a source sync job again.                                                                                    |
| ai\_gateway\_rate\_limited                | AI Gateway rate limited the request.                   | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) later. If the error persists, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).                                                       |

### Storage and Vectorize

These errors appear when storing or indexing an item fails, or when the instance reaches a capacity limit. AI Search uses [Vectorize](https://developers.cloudflare.com/vectorize/) in the background to store vectors and manages it for you.

| Error                            | Details                                                                                              | Recommended action                                                                                                                                                                                                                                                                               |
| -------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| r2\_unknown\_error               | R2 returned an unknown error.                                                                        | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| r2\_internal\_error              | R2 returned an internal error.                                                                       | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists. |
| vectorize\_rate\_limited         | Vectorize rate limited the operation.                                                                | Run a [source sync job](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) later. If the error persists, [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/).                                                                  |
| vectorize\_ingestion\_timeout    | Vectorize did not process the mutation in time.                                                      | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                                        |
| vectorize\_upstream\_error       | Vectorize returned a transient upstream error.                                                       | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                                        |
| hybrid\_search\_indexing\_failed | Hybrid search indexing failed.                                                                       | Sync the item again. Check [Cloudflare Status ↗](https://www.cloudflarestatus.com) and [contact support](https://developers.cloudflare.com/support/contacting-cloudflare-support/) if the error persists.                                                                                        |
| ai\_search\_is\_full             | The AI Search instance is full. Unlike a full hybrid search index, this does not pause the instance. | [Request a higher limit ↗](https://forms.gle/wnizxrEUW33Y15CT8) for the instance, or create another instance to index additional content. See [limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits).                                                             |

## Instance-level errors

These errors pause the whole instance and stop all indexing until the underlying cause is resolved and the instance is resumed. They are distinct from a manual pause or the automatic pause after a period of inactivity.

| Error                                 | Details                                                                                                                                       | Recommended action                                                                                                                                                                                                                         |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| r2\_not\_enabled                      | Pauses the instance. R2 is not enabled for the account.                                                                                       | Enable [R2](https://developers.cloudflare.com/r2/get-started/) for the account, then resume the instance.                                                                                                                                  |
| bucket\_not\_found                    | Pauses the instance. The source R2 bucket was not found.                                                                                      | Check the [R2 data source](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/) bucket name and account, then resume the instance.                                                                                   |
| bucket\_unauthorized                  | Pauses the instance. AI Search cannot access the R2 bucket.                                                                                   | Check the [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) and R2 bucket permissions, then resume the instance.                                                                  |
| external\_source\_missing\_api\_token | Pauses the instance. The external source is missing API credentials.                                                                          | Add or update the [service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/), then resume the instance.                                                                                    |
| bucket\_name\_invalid                 | Pauses the instance. The R2 bucket name is invalid.                                                                                           | Use a valid bucket name without uppercase letters or underscores, update the R2 data source, then resume the instance.                                                                                                                     |
| invalid\_custom\_header               | Pauses the instance. A custom crawl header is invalid.                                                                                        | Remove or update the [extra header](https://developers.cloudflare.com/ai-search/configuration/data-source/website/#extra-headers), then resume the instance.                                                                               |
| ai\_gateway\_not\_configured          | Pauses the instance. The AI Gateway set on the instance was not found.                                                                        | Update the instance's [ai\_gateway\_id](https://developers.cloudflare.com/ai-search/api/instances/rest-api/) to an existing gateway in [AI Gateway](https://developers.cloudflare.com/ai-gateway/), then resume the instance.              |
| hybrid\_search\_is\_full              | Pauses the instance. The hybrid search index is full. Its file limit is lower than the standard instance limit, so it can be reached earlier. | [Request a higher limit ↗](https://forms.gle/wnizxrEUW33Y15CT8) to resume the instance, or create another instance to index additional content. See [limits](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#limits). |

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/#page","headline":"Indexing error codes · Cloudflare AI Search docs","description":"Resolve asynchronous indexing, sync, and crawl error codes.","url":"https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-07-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/ai-search/","name":"AI Search"}},{"@type":"ListItem","position":3,"item":{"@id":"/ai-search/troubleshooting/","name":"Troubleshooting"}},{"@type":"ListItem","position":4,"item":{"@id":"/ai-search/troubleshooting/indexing-error-codes/","name":"Indexing error codes"}}]}
```
