# Web Search

URL: /docs/experiments/web-search
> Web Search API through Cloudflare AI Gateway with Ceramic, Exa, or Linkup - agent-ready search results from the edge
Tags: ai, search, ai-gateway, ceramic, exa, linkup, agents, web-search
Markdown: /llms.mdx/experiments/web-search/content.md



Agent-ready **web search** from a Cloudflare Worker: call the Web Search API through **AI Gateway** using **Ceramic**, **Exa**, or **Linkup** providers. Use it when an agent needs live search results without wiring each search vendor separately.

## API Reference [#api-reference]

### `POST /search` [#post-search]

Search the web via AI Gateway's Web Search API integration.

**Request body**

```json
{
  "query": "cloudflare workers edge computing",
  "provider": "ceramic",
  "limit": 5
}
```

**Fields**

* **`query`** `string` (required) - Search query
* **`provider`** `string` (optional) - Search provider: `ceramic`, `exa`, or `linkup`. Defaults to `ceramic`
* **`limit`** `number` (optional) - Maximum results to return. Defaults to `5`

**Example request**

```bash
curl -X POST https://web-search.example.workers.dev/search \
  -H "Content-Type: application/json" \
  -d '{
    "query": "cloudflare workers edge computing",
    "provider": "ceramic",
    "limit": 5
  }'
```

**Example response**

```json
{
  "query": "cloudflare workers edge computing",
  "provider": "ceramic",
  "limit": 5,
  "results": [
    {
      "url": "https://workers.cloudflare.com",
      "title": "Cloudflare Workers",
      "snippet": "Deploy serverless code instantly across the globe..."
    }
  ]
}
```

**Error responses**

* `400` - Invalid JSON, missing `query`, or invalid `provider`
* `502` - Web Search API error

**Error codes**

* `INVALID_JSON` - Request body is not valid JSON
* `INVALID_REQUEST` - Missing or invalid `query` field
* `INVALID_PROVIDER` - Provider is not one of: ceramic, exa, linkup
* `SEARCH_ERROR` - Web Search API returned an error

## Use Cases [#use-cases]

* Building AI chat agents with web search capabilities
* Research tools that need current web data
* Content discovery and aggregation workflows
* Search-augmented generation (RAG) pipelines
* Educational demos of multi-provider search APIs

## Limitations [#limitations]

* Requires AI Gateway configuration (account ID and gateway ID)
* Web Search API is in beta (Oct 2 2026) - availability and limits may change
* Results limited by the provider's capabilities and coverage
* No persistent storage or search history
* Rate limits determined by AI Gateway and search provider quotas

## Use in your project [#use-in-your-project]

<UseInYourProject experiment="web-search" platform="[&#x22;AI Gateway&#x22;, &#x22;Web Search API&#x22;]" files="[&#x22;src/lib/search.ts&#x22;, &#x22;examples/usage.ts&#x22;]" sources="[{&#x22;path&#x22;:&#x22;src/lib/search.ts&#x22;,&#x22;title&#x22;:&#x22;search.ts&#x22;,&#x22;lang&#x22;:&#x22;ts&#x22;,&#x22;code&#x22;:&#x22;export type SearchProvider = \&#x22;ceramic\&#x22; | \&#x22;exa\&#x22; | \&#x22;linkup\&#x22;;\n\nexport type SearchOptions = {\n  query: string;\n  provider?: SearchProvider;\n  limit?: number;\n  accountId: string;\n  gatewayId: string;\n};\n\nexport async function searchWeb({\n  query,\n  provider = \&#x22;ceramic\&#x22;,\n  limit = 5,\n  accountId,\n  gatewayId,\n}: SearchOptions): Promise<unknown> {\n  const gatewayUrl = `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/web-search/${provider}`;\n\n  const response = await fetch(gatewayUrl, {\n    method: \&#x22;POST\&#x22;,\n    headers: {\n      \&#x22;Content-Type\&#x22;: \&#x22;application/json\&#x22;,\n    },\n    body: JSON.stringify({\n      query,\n      limit,\n    }),\n  });\n\n  if (!response.ok) {\n    const errorText = await response.text();\n    throw new Error(`Web Search API error: ${response.status} ${errorText}`);\n  }\n\n  return response.json();\n}&#x22;},{&#x22;path&#x22;:&#x22;examples/usage.ts&#x22;,&#x22;title&#x22;:&#x22;usage.ts&#x22;,&#x22;lang&#x22;:&#x22;ts&#x22;,&#x22;code&#x22;:&#x22;/**\n * Minimal paste example for docs (\&#x22;Use in your project\&#x22;).\n * Not part of the Worker runtime — copy into your own project.\n */\nimport { searchWeb } from \&#x22;../src/lib/search\&#x22;;\n\nexport default {\n  async fetch(\n    request: Request,\n    env: { AI_GATEWAY_ACCOUNT_ID?: string; AI_GATEWAY_ID?: string }\n  ): Promise<Response> {\n    if (request.method !== \&#x22;POST\&#x22;) {\n      return Response.json(\n        { error: \&#x22;Method not allowed\&#x22;, code: \&#x22;METHOD_NOT_ALLOWED\&#x22; },\n        { status: 405 }\n      );\n    }\n\n    const body = (await request.json()) as {\n      query?: string;\n      provider?: \&#x22;ceramic\&#x22; | \&#x22;exa\&#x22; | \&#x22;linkup\&#x22;;\n      limit?: number;\n    };\n\n    if (!body.query?.trim()) {\n      return Response.json({ error: \&#x22;Missing query\&#x22;, code: \&#x22;INVALID_REQUEST\&#x22; }, { status: 400 });\n    }\n\n    const results = await searchWeb({\n      query: body.query,\n      provider: body.provider,\n      limit: body.limit,\n      accountId: env.AI_GATEWAY_ACCOUNT_ID || \&#x22;demo\&#x22;,\n      gatewayId: env.AI_GATEWAY_ID || \&#x22;web-search-demo\&#x22;,\n    });\n\n    return Response.json({\n      query: body.query,\n      provider: body.provider ?? \&#x22;ceramic\&#x22;,\n      limit: body.limit ?? 5,\n      results,\n    });\n  },\n};&#x22;}]" />

## Deployment [#deployment]

<Steps>
  <Step>
    ### Configure AI Gateway [#configure-ai-gateway]

    Set your AI Gateway credentials in `wrangler.json` or as secrets:

    ```json
    {
      "vars": {
        "AI_GATEWAY_ACCOUNT_ID": "your-account-id",
        "AI_GATEWAY_ID": "your-gateway-id"
      }
    }
    ```

    If not set, the Worker defaults to demo values (limited functionality).
  </Step>

  <Step>
    ### Deploy to Cloudflare Workers [#deploy-to-cloudflare-workers]

    [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/shrinathsnayak/cloudflare-experiments/tree/main/apps/experiments/web-search)

    Update environment variables in the dashboard after deployment.
  </Step>

  <Step>
    ### Test your deployment [#test-your-deployment]

    ```bash
    curl -X POST https://web-search.your-subdomain.workers.dev/search \
      -H "Content-Type: application/json" \
      -d '{
        "query": "cloudflare workers",
        "provider": "ceramic",
        "limit": 5
      }'
    ```
  </Step>
</Steps>

## Local Development [#local-development]

```bash
cd apps/experiments/web-search
npm install
npm run dev
```

The Worker runs at `http://localhost:8787` with `--remote` for Web Search API access.

**Test locally**

```bash
curl -X POST http://localhost:8787/search \
  -H "Content-Type: application/json" \
  -d '{
    "query": "cloudflare workers",
    "provider": "ceramic"
  }'
```

## Configuration [#configuration]

### Environment Variables [#environment-variables]

* **`AI_GATEWAY_ACCOUNT_ID`** - Your Cloudflare account ID
* **`AI_GATEWAY_ID`** - Your AI Gateway ID

Set these in `wrangler.json` under `vars` or as secrets via the Cloudflare dashboard.

## Cloudflare Features Used [#cloudflare-features-used]

* [Workers](https://developers.cloudflare.com/workers/) - Edge compute platform
* [AI Gateway](https://developers.cloudflare.com/ai-gateway/) - Unified API gateway for AI services
* Web Search API (Oct 2026 beta) - Search the web via ceramic, exa, or linkup providers

## Next Steps [#next-steps]

<Cards>
  <Card title="AI Website Summary" href="/experiments/ai-website-summary" description="Summarize webpages with Workers AI" />

  <Card title="RAG Mini Search" href="/experiments/rag-mini-search" description="Grounded Q&A with Vectorize" />

  <Card title="Cloud AI Proxy" href="/experiments/cloud-ai-proxy" description="Call any Workers AI model" />
</Cards>
