# Rate Limits
Source: https://docs.ceramic.ai/admin/rate-limits
Ceramic's Search API rate limits across plans.
Need higher rate limits? [Contact us](mailto:sales@ceramic.ai) for access to higher rate limits with dedicated support.
Our Search API enforces rate limits to ensure reliable performance for all users. Limits are measured in **QPS (Queries Per Second)** and vary by plan.
## Search API Rate Limits
| Plan | Queries Per Second (QPS) |
| ----------------- | ------------------------ |
| **Pay As You Go** | 20 QPS |
| **Pro** | 50 QPS |
| **Enterprise** | Reserved QPS (custom) |
Enterprise plans include **QPS reservations** — dedicated throughput guaranteed for your workload. [Contact us](mailto:sales@ceramic.ai) to discuss your requirements.
# Security and Compliance
Source: https://docs.ceramic.ai/admin/security
How Ceramic protects your data and meets compliance requirements
Security and compliance are foundational to how we build and operate Ceramic. We understand that when you integrate our API into your applications, you're trusting us with your data and your users' trust.
## Compliance
Ceramic has successfully completed SOC 2 Type II certification, validating our security controls and data protection practices through independent third-party audit.
Request access to our SOC 2 Type II report through our [Trust Center](https://app.vanta.com/ceramicinc/trust/bjffwpxz7ln6fk65aigyuo).
## Zero Data Retention (ZDR)
Ceramic.ai is designed with Zero Data Retention in mind. If ZDR is a requirement for your use case, please contact us to discuss your timeline and enterprise options.
## Responsible Disclosure
If you discover a security vulnerability, please report it to [info@ceramic.ai](mailto:info@ceramic.ai). We take all reports seriously and will respond promptly.
## Contact
For enterprise inquiries or custom security requirements, contact [sales@ceramic.ai](mailto:sales@ceramic.ai).
# Team Management
Source: https://docs.ceramic.ai/admin/team-management
Learn how to manage your team in Ceramic
## Inviting team members
To add team members to your workspace:
1. Go to the [**Settings**](https://platform.ceramic.ai/settings) tab on the Platform.
2. Click **Invite Member**
3. Enter the email address and select a role:
* **Admin** - Full account access covering billing, plans, team management, API keys, and usage insights
* **Member** - Access to API keys and usage
## Viewing team members
Members and roles can be seen in the [**Settings**](https://platform.ceramic.ai/settings) tab on the Platform.
## Billing and usage
Billing and usage management are available in the [**Billing**](https://platform.ceramic.ai/billing) and [**Usage**](https://platform.ceramic.ai/usage) tabs on the Platform.
# Error Codes
Source: https://docs.ceramic.ai/api-reference/error-codes
Reference for Ceramic API error codes
Ceramic uses standard HTTP status error codes to indicate the success or failure of API requests.
## HTTP Status Codes
| Code | Meaning | Cause | Retry | Action |
| ---- | --------------------- | ------------------------------------------------ | ----- | ------------------------------------- |
| 200 | Success | Request succeeded | — | Process the response |
| 400 | Invalid Request | Malformed or unparseable request body | No | Fix the request |
| 401 | Unauthorized | Invalid or missing api key | No | Check credentials |
| 402 | Payment Required | Credits exhausted | No | Add credits or upgrade your plan |
| 403 | Forbidden | Plan or account restriction | No | Check plan or account status |
| 404 | Not Found | Path does not exist | No | Verify the request path |
| 405 | Method Not Allowed | HTTP method not supported | No | Use `POST` for `/search` |
| 408 | Request Timeout | Request took too long | Yes | Retry the request |
| 413 | Payload Too Large | Request body exceeds size limit | No | Reduce request size |
| 415 | Unsupported Media | `Content-Type` is not `application/json`. | No | Set `Content-Type: application/json` |
| 422 | Unprocessable Content | Parameter is unsupported, invalid, or wrong type | No | Fix the parameter and retry |
| 429 | Too Many Requests | Rate limit exceeded | Yes | Retry after `retry_after_seconds` |
| 500 | Internal Error | Unexpected server error | Yes | Retry; contact support if it persists |
| 502 | Bad Gateway | Upstream service returned an error | Yes | Retry the request |
| 503 | Service Unavailable | Service temporarily unavailable | Yes | Retry with backoff |
| 504 | Gateway Timeout | Upstream service timed out | Yes | Retry the request |
## Error Response Format
All errors follow the following format. For example, using an unsupported parameter like `prompt` instead of `query`:
```json theme={null}
{
"title": "Unprocessable Content",
"status": 422,
"detail": "Unsupported parameter: prompt",
"requestId": "5e2ef11d-f0e5-407b-ba29-d1b851ed1d65",
"code": "unsupported_parameter"
}
```
## Handling Errors
```python python theme={null}
from ceramic_ai import Ceramic
client = Ceramic(api_key="YOUR_API_KEY")
try:
client.search(query="California rental laws")
except ceramic_ai.APIStatusError as e:
print(f"HTTP {e.status_code}")
print("body:", e.body)
except ceramic_ai.APIConnectionError as e:
print("Connection error:", str(e))
```
```typescript javascript theme={null}
import Ceramic from "ceramic-ai";
const client = new Ceramic({ apiKey: "YOUR_API_KEY" });
async function main() {
try {
const response = await client.search({ query: "California rental laws" });
console.log(response.requestId);
} catch (err) {
if (err instanceof Ceramic.APIConnectionError) {
console.log("Connection error:", err.message);
} else if (err instanceof Ceramic.APIError) {
console.log(HTTP ${err.status});
console.log("body:", err.error);
} else {
throw err;
}
}
}
main();
```
## Retry Strategy
Both the Python and TypeScript SDKs automatically retry transient failures with a short exponential backoff.
By default, the SDK retries **2 times** on:
* network/connection errors
* **408** Request Timeout
* **429** Rate Limit
* **5xx** server errors
You can disable or tune retries via the client option (`max_retries` in Python, `maxRetries` in TypeScript), or per-request.
```python python theme={null}
from ceramic_ai import Ceramic
client = Ceramic(
api_key="YOUR_API_KEY",
max_retries=0, # disable retries (default is 2)
)
# or per-request
client.with_options(max_retries=5).search(query="California rental laws")
```
```typescript javascript theme={null}
import Ceramic from "ceramic-ai";
const client = new Ceramic({
apiKey: "YOUR_API_KEY",
maxRetries: 0, // disable retries (default is 2)
});
// or per-request
await client.search({ query: "California rental laws" }, { maxRetries: 5 });
```
# Search
Source: https://docs.ceramic.ai/api-reference/search
post /search
Search the web and retrieve relevant content.
# Best Practices
Source: https://docs.ceramic.ai/api/search/best-practices
Best practices for using Ceramic Search API
Ceramic is a **lexical search engine** built from the ground up for AI. Understanding how lexical search works and following these guidelines will help you get the most relevant results from Ceramic Search.
## What is Lexical Search?
Ceramic uses **lexical (keyword-based) search**, which matches documents based on the **exact words and phrases** in your query.
Unlike semantic search systems, Ceramic does not infer meaning, intent, or synonyms—it focuses on precise term matching for speed, transparency, and control.
#### Key characteristics
* Matches **exact keywords and phrases**
* Fast and computationally efficient
* Does **not automatically handle synonyms or intent**
## Why Keyword Search Works Well with LLMs
While Ceramic uses keyword-based (lexical) search, this approach works especially well when combined with LLMs.
#### LLMs are good at query generation
LLMs can transform natural language into high-quality keyword queries. Instead of relying on the search system to interpret intent, you can use an LLM to:
* Rewrite queries into precise keyword-based searches
* Expand queries with synonyms or related terms
* Generate multiple variations of a query
***
#### Search becomes a plentiful resource
Many search systems treat search as a **scarce and expensive operation**, encouraging a single, highly optimized query.
Ceramic takes a different approach.
With a Search API at very low cost, we found more value in creating several keyword-based searches rather than one overemphasized query.
This enables new patterns:
* Multi-query retrieval (increase recall)
* Query variation (capture different terminology)
* Iterative refinement (improve results over time)
***
#### Example: single query vs multi-query
**Instead of one over-optimized query:**
`impact of climate change on agriculture in developing countries`
**You can issue multiple simpler queries:**
`climate change agriculture impact`
`global warming crop yields`
`developing countries farming climate effects`
***
#### Why this works
* Lexical search provides **fast, precise matching**
* LLMs provide **understanding and query generation**
* Combining both gives you **control + flexibility**
***
#### Recommended approach
For LLM-powered applications:
1. Start with a user query
2. Use an LLM to generate multiple keyword-based queries
3. Send each query to Ceramic
4. Aggregate and rank the results
This often produces better results than relying on a single complex query.
## What Works Well
Lexical search performs best when your query includes **specific, well-defined terms** that are likely to appear in the target documents.
#### Good Query Patterns
* Exact names
* Technical terms
* Product names or identifiers
* Specific phrases
| Use case | Example query |
| ------------------------- | ----------------------------------------------- |
| `Person lookup` | `Serena Williams Grand Slam titles` |
| `Technical documentation` | `OAuth 2.0` |
| `Legal text` | `California tenant security deposit return law` |
| `News/event lookup` | `2026 Super bowl halftime performer` |
## What Doesn’t Work Well
Because Ceramic does not perform semantic understanding, it may struggle with:
#### 1. Synonyms
Different words with the same meaning are not automatically matched
* BBQ ≠ barbecue
* gym ≠ fitness center
For example, a query for `gym membership cost` may miss results that use `fitness center pricing`.
#### 2. Vague or abstract queries
Queries without clear keywords may return weak or irrelevant results.
| Query | Issue |
| -------------------------- | ---------------------- |
| `technology trends` | `Too broad` |
| `how people feel about AI` | `Lacks concrete terms` |
#### 3. Natural language / conversational queries
Ceramic does not interpret intent like an LLM or semantic search system.
| Query | Issue |
| --------------------------------------------------- | ------------------------------------------ |
| `What are the best ways to invest money right now?` | `Too conversational` |
| `Why is rent so high in California?` | `Requires reasoning, not keyword matching` |
#### 4. Misspellings or loosely related terms
Lexical search relies on exact matching, so spelling and wording matter.
## How to Write Effective Queries
Specific queries return better results than broad ones. Include relevant context and details.
#### Be specific
Include **important keywords, entities, and context**
| Instead of | Try |
| ----------------- | ------------------------------------------------ |
| `technology news` | `OpenAI GPT-5 announcement 2025` |
| `California laws` | `California tenant security deposit return laws` |
#### Include multiple relevant terms
More context = better matching
| Instead of | Try |
| ---------- | ------------------------------------------ |
| `climate` | `climate change policy United States 2024` |
#### Use explicit synonyms when needed
If you’re unsure which term appears in documents, include multiple:
`college university tuition costs US`
#### Word order matters
The order of words in your query affects results. The same words in different orders can return different results.
| Query | Finds |
| ----------- | ------------------------- |
| `cat house` | Outdoor shelters for cats |
| `house cat` | Domestic cats as pets |
## Using Ceramic with LLMs (Query Rewriting)
If you're using Ceramic in an AI or RAG pipeline, you should **rewrite user queries into keyword-focused search queries** before sending them to the API.
LLMs are great at this transformation.
### Example
**User query (natural language):**
`Why is rent so high in California right now?`
**Rewritten query (Ceramic-optimized):**
`California rent increase causes housing shortage 2025`
***
### Prompt Template
Use the following prompt to convert user input into effective Ceramic queries:
```
Rewrite the following user query into a concise, keyword-based search query optimized for a lexical search engine.
Guidelines:
- Use specific keywords and entities
- Avoid conversational language
- Include relevant context (location, date, topic)
- Do not include full sentences
- Output only the rewritten query
User query:
{user_query}
```
***
### Example Transformations
| User Query | Rewritten Query |
| -------------------------------------------- | --------------------------------------------------- |
| `What are the effects of climate change?` | `climate change effects global warming impact` |
| `Who performed at the Super Bowl this year?` | `2026 Super Bowl halftime performer` |
| `How do I start investing?` | `beginner investing strategies stocks bonds basics` |
***
### Why this matters
Ceramic does not interpret intent like an LLM. Rewriting queries helps:
* Improve keyword matching
* Produce more consistent and predictable results
## Controlling Result Description Length
Each search result includes a description — a snippet of text from the matched document. You can control the maximum character length of these descriptions using the `maxDescriptionLength` parameter.
**Default:** `3000` characters, which works well for most use cases.
**Range:** `1000` – `8000` characters.
### When to adjust it
| Situation | Recommendation |
| --------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Feeding results into an LLM with a limited context window | Lower it (e.g. `1000`–`1500`) to fit more results without exceeding token limits |
| Deep document analysis where full context matters | Raise it (e.g. `6000`–`8000`) to retain more of the source text |
| Multi-query retrieval with many results | Lower it so the combined output stays within your context budget |
Start with the default `3000` and adjust based on how many results you're passing to your model and what context window you're working within.
***
## When NOT to Use Ceramic Alone
You may want to combine Ceramic with other systems if your use case requires:
* Understanding intent or meaning
* Handling natural language questions
* Matching concepts rather than exact words
We're actively exploring semantic and hybrid retrieval patterns—if this applies to your use case, reach out to our team at [support](mailto:support@ceramic.ai).
## Language support
Ceramic currently supports English web pages. Support for additional languages is coming soon.
# Quickstart
Source: https://docs.ceramic.ai/api/search/quickstart
Get started with Ceramic Search in whatever way you choose, such as via the playground, API, or one of the many integrations.
## Get your API key
Generate your API key on the [Platform](https://platform.ceramic.ai/keys).
1,000 free credits when you sign up.
No setup required. Send queries to Ceramic Search directly in your browser.
Make a request directly from your terminal.
```bash theme={null}
curl https://api.ceramic.ai/search \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "California rental laws"}'
```
Install the SDK for your language.
```bash python theme={null}
pip install ceramic_ai
```
```bash typescript theme={null}
npm install ceramic-ai
```
Make your first request.
```python python theme={null}
from ceramic_ai import Ceramic
client = Ceramic(api_key="YOUR_API_KEY")
response = client.search(query="California rental laws")
print(response)
```
```typescript typescript theme={null}
import { Ceramic } from "ceramic-ai";
const client = new Ceramic({ apiKey: "YOUR_API_KEY" });
const response = await client.search({ query: "California rental laws"});
console.log(response);
```
### Response
```json theme={null}
{
"requestId": "ae2ebd93-194f-4460-9996-15e3f86b05d8",
"result": {
"results": [
{
"title": "California Tenant Rights Guide",
"url": "https://example.com/tenant-rights",
"description": "Comprehensive guide to California rental laws..."
}
],
"searchMetadata": {
"executionTime": 0.097
},
"totalResults": 10
}
}
```
Connect Ceramic Search to your AI coding assistants and agents.
## Integrations
Use Ceramic Search alongside the tools you already use.
* [Anthropic Tool Use](/integrations/anthropic)
* [CrewAI](/integrations/crewai)
* [LangChain](/integrations/langchain)
* [LlamaIndex](/integrations/llamaindex)
* [OpenAI Tool Use](/integrations/openai)
* [OpenClaw](/integrations/openclaw)
* [Vercel AI SDK](/integrations/vercel)
## Next steps
Explore all available parameters
Get the most out of Ceramic Search
# Anthropic Tool Use
Source: https://docs.ceramic.ai/integrations/anthropic
Use Ceramic Search as a tool with Claude to build search-powered responses and agents
Claude's tool use lets a model call functions you define. This guide shows how to wire Ceramic Search as a client tool, so Claude can retrieve real-time web results when generating a response.
Create a free account to get started.
## Generating model responses
When Claude needs to search, it returns a `tool_use` block with `stop_reason: "tool_use"`. You execute the search, append the result as a `tool_result` in a user message, and call the API again for the final answer.
### Set environment variables
```bash theme={null}
export CERAMIC_API_KEY=your_ceramic_api_key
export ANTHROPIC_API_KEY=your_anthropic_api_key
```
### Install dependencies
```bash python theme={null}
pip install anthropic ceramic_ai
```
```bash typescript theme={null}
npm install @anthropic-ai/sdk ceramic-ai
```
### Full example
```python python theme={null}
import os
import anthropic
from ceramic_ai import Ceramic
claude = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))
TOOL_DESCRIPTION = (
"Search the web using Ceramic.\n"
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n"
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n"
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n"
"Query rules:\n"
"- Queries must be 2-8 words\n"
"- Include specific entities, topics, locations, and dates\n"
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n"
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n"
"- Good keyword query examples:\n"
" - \"2026 Super Bowl halftime performer\"\n"
" - \"climate change effects global warming impact\"\n"
" - \"beginner investing strategies stocks bonds basics\"\n"
"If the search returns no useful results, retry with a more specific keyword query."
)
ceramic_search_tool = {
"name": "ceramic_search",
"description": TOOL_DESCRIPTION,
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "keyword search query with 2–8 words",
}
},
"required": ["query"],
},
}
SYSTEM = "You have access to a web search tool. Use it to answer questions with up-to-date information."
messages = [{"role": "user", "content": "What are the latest California tenant protection laws?"}]
response = claude.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=SYSTEM,
messages=messages,
tools=[ceramic_search_tool],
)
while response.stop_reason == "tool_use":
tool_use_blocks = [block for block in response.content if block.type == "tool_use"]
# Append the full assistant message
messages.append({"role": "assistant", "content": response.content})
# Execute each tool call and collect results
tool_results = []
for tool_use in tool_use_blocks:
if tool_use.name == "ceramic_search":
results = ceramic.search(query=tool_use.input["query"])
tool_results.append({
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": str(results),
})
messages.append({"role": "user", "content": tool_results})
response = claude.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=SYSTEM,
messages=messages,
tools=[ceramic_search_tool],
)
print(next((block.text for block in response.content if block.type == "text"), ""))
```
```typescript typescript theme={null}
import Anthropic from "@anthropic-ai/sdk";
import { Ceramic } from "ceramic-ai";
const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const ceramic = new Ceramic({ apiKey: process.env.CERAMIC_API_KEY });
const TOOL_DESCRIPTION =
"Search the web using Ceramic.\n" +
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n" +
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n" +
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n" +
"Query rules:\n" +
"- Queries must be 2-8 words\n" +
"- Include specific entities, topics, locations, and dates\n" +
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n" +
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n" +
"- Good keyword query examples:\n" +
' - "2026 Super Bowl halftime performer"\n' +
' - "climate change effects global warming impact"\n' +
' - "beginner investing strategies stocks bonds basics"\n' +
"If the search returns no useful results, retry with a more specific keyword query.";
const ceramicSearchTool: Anthropic.Tool = {
name: "ceramic_search",
description: TOOL_DESCRIPTION,
input_schema: {
type: "object",
properties: {
query: {
type: "string",
description: "keyword search query with 2–8 words",
},
},
required: ["query"],
},
};
const SYSTEM = "You have access to a web search tool. Use it to answer questions with up-to-date information.";
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: "What are the latest California tenant protection laws?" },
];
let response = await claude.messages.create({
model: "claude-opus-4-6",
max_tokens: 1024,
system: SYSTEM,
messages,
tools: [ceramicSearchTool],
});
while (response.stop_reason === "tool_use") {
const toolUseBlocks = response.content.filter(
(block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
);
// Append the full assistant message
messages.push({ role: "assistant", content: response.content });
// Execute each tool call and collect results
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const toolUse of toolUseBlocks) {
if (toolUse.name === "ceramic_search") {
const input = toolUse.input as { query: string };
const results = await ceramic.search({ query: input.query });
toolResults.push({
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify(results),
});
}
}
messages.push({ role: "user", content: toolResults });
response = await claude.messages.create({
model: "claude-opus-4-6",
max_tokens: 1024,
system: SYSTEM,
messages,
tools: [ceramicSearchTool],
});
}
const textBlock = response.content.find(
(block): block is Anthropic.TextBlock => block.type === "text"
);
if (textBlock) console.log(textBlock.text);
```
### Run the example
```bash python theme={null}
python anthropic_tool_calling.py
```
```bash typescript theme={null}
npx tsx anthropic_tool_calling.ts
```
## Building agents
The Tool Runner handles the tool-calling loop automatically. Define `ceramic_search` with the `@beta_tool` decorator (Python) or `betaZodTool` (TypeScript) and the SDK executes it and continues the conversation until Claude returns a final answer.
### Set environment variables
```bash theme={null}
export CERAMIC_API_KEY=your_ceramic_api_key
export ANTHROPIC_API_KEY=your_anthropic_api_key
```
### Install dependencies
```bash python theme={null}
pip install anthropic ceramic_ai
```
```bash typescript theme={null}
npm install @anthropic-ai/sdk ceramic-ai zod
```
### Full example
```python python theme={null}
import os
import anthropic
from anthropic import beta_tool
from ceramic_ai import Ceramic
claude = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))
@beta_tool
def ceramic_search(query: str) -> str:
"""Search the web using Ceramic.
Use for accurate current information — news, prices, recent events, documentation, general fact checking.
Returns up to 10 ranked results with titles, URLs, and descriptions.
Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.
Query rules:
- Queries must be 2-8 words
- Include specific entities, topics, locations, and dates
- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).
- Keep word order meaningful (`house cat` and `cat house` return different results)
- Good keyword query examples:
- "2026 Super Bowl halftime performer"
- "climate change effects global warming impact"
- "beginner investing strategies stocks bonds basics"
If the search returns no useful results, retry with a more specific keyword query.
Args:
query: keyword search query with 2–8 words
"""
results = ceramic.search(query=query)
return str(results)
runner = claude.beta.messages.tool_runner(
model="claude-opus-4-6",
max_tokens=1024,
tools=[ceramic_search],
messages=[{"role": "user", "content": "What are the latest California tenant protection laws?"}],
)
final_message = runner.until_done()
for block in final_message.content:
if block.type == "text":
print(block.text)
```
```typescript typescript theme={null}
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { Ceramic } from "ceramic-ai";
import { z } from "zod";
const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const ceramic = new Ceramic({ apiKey: process.env.CERAMIC_API_KEY });
const TOOL_DESCRIPTION =
"Search the web using Ceramic.\n" +
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n" +
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n" +
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n" +
"Query rules:\n" +
"- Queries must be 2-8 words\n" +
"- Include specific entities, topics, locations, and dates\n" +
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n" +
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n" +
"- Good keyword query examples:\n" +
' - "2026 Super Bowl halftime performer"\n' +
' - "climate change effects global warming impact"\n' +
' - "beginner investing strategies stocks bonds basics"\n' +
"If the search returns no useful results, retry with a more specific keyword query.";
const ceramicSearchTool = betaZodTool({
name: "ceramic_search",
description: TOOL_DESCRIPTION,
inputSchema: z.object({
query: z.string().describe("keyword search query with 2–8 words"),
}),
run: async ({ query }) => {
const results = await ceramic.search({ query });
return JSON.stringify(results);
},
});
const finalMessage = await claude.beta.messages.toolRunner({
model: "claude-opus-4-6",
max_tokens: 1024,
tools: [ceramicSearchTool],
messages: [{ role: "user", content: "What are the latest California tenant protection laws?" }],
});
for (const block of finalMessage.content) {
if (block.type === "text") {
console.log(block.text);
}
}
```
### Run the example
```bash python theme={null}
python tool_runner.py
```
```bash typescript theme={null}
npx tsx tool_runner.ts
```
# CrewAI
Source: https://docs.ceramic.ai/integrations/crewai
Add Ceramic search to your CrewAI agents
We explain how to integrate Ceramic Search with CrewAI by building a two-agent crew: one that researches a topic using Ceramic search and one that writes an article from those results.
## Setup
### 1. Installation
Install CrewAI and the Ceramic Python SDK:
```bash theme={null}
pip install crewai 'crewai[tools]' ceramic_ai
```
### 2. API key
Obtain and set your Ceramic API key:
Create a Ceramic account for free to get an API key.
```bash theme={null}
export CERAMIC_API_KEY=your_api_key_here
```
To persist it across sessions, add the line above to your `~/.zshrc`, `~/.bashrc`, or equivalent.
Also set up any additional API keys you need, e.g., OpenAI:
```bash theme={null}
export OPENAI_API_KEY=your_api_key_here
```
### 3. Define a custom Ceramic tool
Use the CrewAI [`@tool` decorator](https://docs.crewai.com/concepts/tools#utilizing-the-tool-decorator) to wrap the Ceramic SDK. Initialize the client, run a search, and return formatted results the agent can reason over.
```python theme={null}
from crewai.tools import tool
from ceramic_ai import Ceramic
import os
ceramic_api_key = os.getenv("CERAMIC_API_KEY")
@tool
def ceramic_search_tool(question: str) -> str:
"""
Search the web using Ceramic.
Use for accurate current information — news, prices, recent events, documentation, general fact checking.
Returns up to 10 ranked results with titles, URLs, and descriptions.
Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically. Queries must be keyword-based.
Keyword query conversion rules:
- Queries must be 2-8 words
- Extract specific entities, topics, locations, and dates
- Replace conversational phrasing with concrete keywords
- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).
- Include relevant synonyms explicitly when terminology is ambiguous
- Keep word order meaningful (`house cat` and `cat house` return different results)
- Good keyword query examples:
- "2026 Super Bowl halftime performer"
- "climate change effects global warming impact"
- "beginner investing strategies stocks bonds basics"
If the search returns no useful results, retry with a more specific keyword query.
"""
client = Ceramic(api_key=ceramic_api_key)
response = client.search(query=question)
parsed = ''.join([
f'
{r.title}'
f'{r.url}'
f'{r.description[:300]}'
for idx, r in enumerate(response.result.results)
])
return parsed
```
### 4. Set up agents
Import the relevant CrewAI modules and assign `ceramic_tools` to the custom search function defined above.
```python theme={null}
from crewai import Task, Crew, Agent
ceramic_tools = ceramic_search_tool
```
Define two agents — one to research using Ceramic search, another to write an article from those results:
```python theme={null}
researcher = Agent(
role='Researcher',
goal='Get the latest information on {topic}',
verbose=True,
memory=True,
backstory=(
"Driven by curiosity, you're at the forefront of"
"innovation, eager to explore and share knowledge."
),
tools=[ceramic_tools],
allow_delegation=False
)
writer = Agent(
role='Writer',
goal='Write a great article on {topic}',
verbose=True,
memory=True,
backstory=(
"Driven by a love of writing, you are eager to"
"share knowledge clearly and engagingly."
),
tools=[ceramic_tools],
allow_delegation=False
)
```
### 5. Define tasks and create the crew
Assign tasks to each agent and assemble them into a crew:
```python theme={null}
research_task = Task(
description=(
"Identify the latest information on {topic}. "
"Your final report should clearly articulate the key points."
),
expected_output='A comprehensive 3 paragraph report on {topic}.',
tools=[ceramic_tools],
agent=researcher,
)
write_article = Task(
description=(
"Write an article on the latest findings about {topic}."
"Your article should be engaging, informative, and accurate."
),
expected_output='A comprehensive 3 paragraph article on {topic}.',
agent=writer,
)
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_article],
memory=True,
cache=True,
max_rpm=100,
share_crew=True
)
```
### 6. Kick off the crew
Provide a topic as input and run the crew:
```python theme={null}
response = crew.kickoff(inputs={'topic': 'California rental laws'})
print(response)
```
### 7. Output
Ceramic's search results enrich the agent's output with relevant, up-to-date sources:
```
California’s rental laws in 2026 have undergone significant updates aimed at
strengthening tenant protections and ensuring fair landlord practices amid the
state’s ongoing housing challenges. Central to these changes is the continued
influence of the California Tenant Protection Act of 2019, which maintains
strict statewide rent control measures. This law limits annual rent increases
and shields tenants from unjust evictions, cementing California’s position as
one of the most tenant-friendly states in the nation. Furthermore, cities such
as Oakland and San Leandro supplement these protections with their own local
rent control ordinances, requiring landlords to navigate a complex regulatory
environment that balances tenant rights with landlord obligations.
[response continues]
```
View CrewAI documentation
View Ceramic Python SDK
# LangChain
Source: https://docs.ceramic.ai/integrations/langchain
Use Ceramic Search within LangChain
We explain how to integrate Ceramic Search with LangChain to build RAG pipelines and ground agent responses in high-quality web search results.
## Installation
```bash theme={null}
pip install langchain langchain-openai langchain-ceramic
```
## API keys
Get your Ceramic API key and set it as an environment variable:
Create a Ceramic account for free to get an API key.
```bash theme={null}
export CERAMIC_API_KEY="your-api-key"
```
Also set up any additional API keys you need, e.g., OpenAI via
```bash theme={null}
export OPENAI_API_KEY="your-api-key"
```
## Example usage
### Tool calling
LangChain agents can use Ceramic search via tool calling to support their response with sources from the web.
Ceramic uses lexical (keyword-based) search. See [Best Practices](https://docs.ceramic.ai/api/search/best-practices) for information on how to use Ceramic Search most effectively. When calling Ceramic search via a tool call, the LLM automatically converts the natural language query into an optimized keyword-based query for search.
```python theme={null}
from langchain_ceramic import CeramicSearch
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
# Initialize the Ceramic search tool and retrieve a maximum of five results
ceramic_search = CeramicSearch(max_results=5)
# Initialize the agent with the Ceramic search tool
agent = create_agent(
model=ChatOpenAI(model="gpt-5.5"),
tools=[ceramic_search],
system_prompt="You are a helpful research assistant. Use web search to find accurate, up-to-date information."
)
# Generate a response using natural language queries
result = agent.invoke(
{"messages": [{"role": "user", "content": "Tell me about California rental laws."}]}
)
print(result["messages"][-1].content)
```
### RAG pipeline
Use the retriever tool `CeramicSearchRetriever` to obtain relevant documents for RAG pipelines.
Because Ceramic uses lexical search, we first convert the natural language query into keywords using an LLM before retrieval. The original natural language query is still passed through to the answer prompt.
```python theme={null}
from langchain_ceramic import CeramicSearchRetriever
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
# Initialize the LLM and Ceramic Search Retriever
llm = ChatOpenAI(model="gpt-5.5")
retriever = CeramicSearchRetriever(k=5)
# Convert the natural language query to keywords before retrieval
keyword_prompt = PromptTemplate.from_template(
"""
Rewrite the following question as a 2-8 word keyword query for a lexical search engine.
Rules:
- Extract specific entities, topics, locations, and dates
- Replace conversational phrasing with concrete keywords
- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).
- Include relevant synonyms explicitly when terminology is ambiguous
- Keep word order meaningful (`house cat` and `cat house` return different results)
- Good keyword query examples:
- "2026 Super Bowl halftime performer"
- "climate change effects global warming impact"
- "beginner investing strategies stocks bonds basics"
Return only the keyword query with no explanation.
Question: {query}
"""
)
keyword_chain = keyword_prompt | llm | StrOutputParser()
# Format the prompt with the query and retrieved search context
answer_prompt = ChatPromptTemplate.from_template(
"Answer the query based on the provided context.\n\nQuery: {query}\n\nContext: {context}"
)
# Create the complete chain, which involves keyword_chain and passes the formatted prompt to the LLM
# RunnablePassthrough() preserves the natural language query for the answer prompt
chain = (
{"query": RunnablePassthrough(), "context": keyword_chain | retriever}
| answer_prompt
| llm
| StrOutputParser()
)
# Generate the response
answer = chain.invoke("What are the latest AI chip export restrictions?")
print(answer)
```
Each retrieved `Document` has:
* `page_content`: the result description
* `metadata["title"]`: page title
* `metadata["url"]`: source URL
### Async usage
Both `CeramicSearchRetriever` and `CeramicSearch` support async:
```python theme={null}
docs = await retriever.ainvoke("California rental laws")
```
## Parameters
### `CeramicSearch`
| Parameter | Type | Description | Default |
| ------------- | ------------- | ----------------------------------------------------------- | ------- |
| `api_key` | `str \| None` | Ceramic API key (falls back to `CERAMIC_API_KEY` env var) | `None` |
| `max_results` | `int` | Maximum number of results to include in the response string | `5` |
### `CeramicSearchRetriever`
| Parameter | Type | Description | Default |
| --------- | ------------- | --------------------------------------------------------- | ------- |
| `api_key` | `str \| None` | Ceramic API key (falls back to `CERAMIC_API_KEY` env var) | `None` |
| `k` | `int` | Maximum number of results to return | `10` |
View source code
View package
# LlamaIndex
Source: https://docs.ceramic.ai/integrations/llamaindex
Use Ceramic Search as a tool in LlamaIndex agents
LlamaIndex is a framework for building LLM-powered applications. This guide shows how to use the `llama-index-tools-ceramic` package to give a LlamaIndex agent access to Ceramic Search.
Create a free account to get started.
## Building agents
The `CeramicToolSpec` wraps Ceramic Search as a LlamaIndex tool. Pass it to any LlamaIndex agent and the agent will call it automatically when it needs to retrieve information.
### Set environment variables
```bash theme={null}
export CERAMIC_API_KEY=your_ceramic_api_key
export OPENAI_API_KEY=your_openai_api_key
```
### Install dependencies
```bash python theme={null}
pip install llama-index-tools-ceramic llama-index-llms-openai
```
### Full example
```python python theme={null}
import asyncio
import os
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
from llama_index.tools.ceramic import CeramicToolSpec
ceramic_tool = CeramicToolSpec(api_key=os.environ["CERAMIC_API_KEY"])
agent = FunctionAgent(
tools=ceramic_tool.to_tool_list(),
llm=OpenAI(model="gpt-5.4"),
)
async def main():
response = await agent.run("What are the latest California tenant protection laws?")
print(response)
asyncio.run(main())
```
### Run the example
```bash python theme={null}
python llamaindex_agent.py
```
View source code
View package
# OpenAI Tool Use
Source: https://docs.ceramic.ai/integrations/openai
Use Ceramic Search as a tool with OpenAI to build search-powered responses and agents
OpenAI’s tool calling lets a model invoke functions you define. This guide shows how to implement a Ceramic Search function the model can use to retrieve real-time web results when generating a response.
Create a free account to get started.
## Generating model responses
Ceramic Search works with both the Chat Completions API and the Responses API. Select the one that matches your project.
### Set environment variables
```bash theme={null}
export CERAMIC_API_KEY=your_ceramic_api_key
export OPENAI_API_KEY=your_openai_api_key
```
### Install dependencies
```bash python theme={null}
pip install openai ceramic_ai
```
```bash typescript theme={null}
npm install openai ceramic-ai
```
### Full example
When the model decides a search is needed, it returns a `tool_calls` message. You execute the search, return the results, and call the model again to get the final answer.
```python python theme={null}
import os
import json
from openai import OpenAI
from ceramic_ai import Ceramic
openai = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))
TOOL_DESCRIPTION = (
"Search the web using Ceramic.\n"
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n"
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n"
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n"
"Query rules:\n"
"- Queries must be 2-8 words\n"
"- Include specific entities, topics, locations, and dates\n"
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n"
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n"
"- Good keyword query examples:\n"
" - \"2026 Super Bowl halftime performer\"\n"
" - \"climate change effects global warming impact\"\n"
" - \"beginner investing strategies stocks bonds basics\"\n"
"If the search returns no useful results, retry with a more specific keyword query."
)
ceramic_search_tool = {
"type": "function",
"function": {
"name": "ceramic_search",
"description": TOOL_DESCRIPTION,
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "keyword search query with 2–8 words",
}
},
"required": ["query"],
},
},
}
messages = [
{"role": "user", "content": "What are the latest California tenant protection laws?"}
]
# First call — model may decide to search
response = openai.chat.completions.create(
model="gpt-5.4",
messages=messages,
tools=[ceramic_search_tool],
tool_choice="auto",
)
message = response.choices[0].message
if message.tool_calls:
messages.append(message)
for tool_call in message.tool_calls:
args = json.loads(tool_call.function.arguments)
results = ceramic.search(query=args["query"])
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(results),
}
)
# Second call — model generates the final answer
final_response = openai.chat.completions.create(
model="gpt-5.4",
messages=messages,
)
print(final_response.choices[0].message.content)
else:
print(message.content)
```
```typescript typescript theme={null}
import OpenAI from "openai";
import { Ceramic } from "ceramic-ai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const ceramic = new Ceramic({ apiKey: process.env.CERAMIC_API_KEY });
const TOOL_DESCRIPTION =
"Search the web using Ceramic.\n" +
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n" +
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n" +
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n" +
"Query rules:\n" +
"- Queries must be 2-8 words\n" +
"- Include specific entities, topics, locations, and dates\n" +
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n" +
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n" +
"- Good keyword query examples:\n" +
' - "2026 Super Bowl halftime performer"\n' +
' - "climate change effects global warming impact"\n' +
' - "beginner investing strategies stocks bonds basics"\n' +
"If the search returns no useful results, retry with a more specific keyword query.";
const ceramicSearchTool: OpenAI.ChatCompletionTool = {
type: "function",
function: {
name: "ceramic_search",
description: TOOL_DESCRIPTION,
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "keyword search query with 2–8 words",
},
},
required: ["query"],
},
},
};
const messages: OpenAI.ChatCompletionMessageParam[] = [
{ role: "user", content: "What are the latest California tenant protection laws?" },
];
// First call — model may decide to search
const response = await openai.chat.completions.create({
model: "gpt-5.4",
messages,
tools: [ceramicSearchTool],
tool_choice: "auto",
});
const message = response.choices[0].message;
if (message.tool_calls) {
messages.push(message);
for (const toolCall of message.tool_calls) {
const args = JSON.parse(toolCall.function.arguments);
const results = await ceramic.search({ query: args.query });
messages.push({
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(results),
});
}
// Second call — model generates the final answer
const finalResponse = await openai.chat.completions.create({
model: "gpt-5.4",
messages,
});
console.log(finalResponse.choices[0].message.content);
} else {
console.log(message.content);
}
```
Maintain a running `input_list` starting with the user message. After the first call, append all output items to the list. When you find a `function_call` item, execute the search and append a `function_call_output` before calling the API again for the final answer.
```python python theme={null}
import os
import json
from openai import OpenAI
from ceramic_ai import Ceramic
openai = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))
TOOL_DESCRIPTION = (
"Search the web using Ceramic.\n"
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n"
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n"
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n"
"Query rules:\n"
"- Queries must be 2-8 words\n"
"- Include specific entities, topics, locations, and dates\n"
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n"
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n"
"- Good keyword query examples:\n"
" - \"2026 Super Bowl halftime performer\"\n"
" - \"climate change effects global warming impact\"\n"
" - \"beginner investing strategies stocks bonds basics\"\n"
"If the search returns no useful results, retry with a more specific keyword query."
)
TOOLS = [
{
"type": "function",
"name": "ceramic_search",
"description": TOOL_DESCRIPTION,
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "keyword search query with 2–8 words",
}
},
"required": ["query"],
},
}
]
user_input = "What are the latest California tenant protection laws?"
input_list = [{"role": "user", "content": user_input}]
while True:
response = openai.responses.create(
model="gpt-5.4",
input=input_list,
tools=TOOLS,
)
input_list += response.output
tool_calls = [item for item in response.output if item.type == "function_call"]
if not tool_calls:
print(response.output_text)
break
for item in tool_calls:
if item.name == "ceramic_search":
query = json.loads(item.arguments)["query"]
results = ceramic.search(query=query)
input_list.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": str(results),
})
```
```typescript typescript theme={null}
import OpenAI from "openai";
import { Ceramic } from "ceramic-ai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const ceramic = new Ceramic({ apiKey: process.env.CERAMIC_API_KEY });
const TOOL_DESCRIPTION =
"Search the web using Ceramic.\n" +
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n" +
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n" +
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n" +
"Query rules:\n" +
"- Queries must be 2-8 words\n" +
"- Include specific entities, topics, locations, and dates\n" +
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n" +
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n" +
"- Good keyword query examples:\n" +
' - "2026 Super Bowl halftime performer"\n' +
' - "climate change effects global warming impact"\n' +
' - "beginner investing strategies stocks bonds basics"\n' +
"If the search returns no useful results, retry with a more specific keyword query.";
const tools: OpenAI.Responses.Tool[] = [
{
type: "function",
name: "ceramic_search",
description: TOOL_DESCRIPTION,
strict: true,
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "keyword search query with 2–8 words",
},
},
required: ["query"],
additionalProperties: false,
},
},
];
const userInput = "What are the latest California tenant protection laws?";
const inputList: OpenAI.Responses.ResponseInputParam[] = [
{ role: "user", content: userInput },
];
while (true) {
const response = await openai.responses.create({
model: "gpt-5.4",
input: inputList,
tools,
});
inputList.push(...response.output);
const toolCalls = response.output.filter((item) => item.type === "function_call");
if (toolCalls.length === 0) {
console.log(response.output_text);
break;
}
for (const item of toolCalls) {
if (item.type === "function_call" && item.name === "ceramic_search") {
const args = JSON.parse(item.arguments);
const results = await ceramic.search({ query: args.query });
inputList.push({
type: "function_call_output",
call_id: item.call_id,
output: JSON.stringify(results),
});
}
}
}
```
### Run the example
Save the code to a file, then run it from your terminal:
```bash python theme={null}
python openai_tool_calling.py
```
```bash typescript theme={null}
npx tsx openai_tool_calling.ts
```
## Building agents
In the Agents SDK, wrap Ceramic Search as a `function_tool` and attach it to your agent. The SDK handles the tool-calling loop automatically.
### Set environment variables
```bash theme={null}
export CERAMIC_API_KEY=your_ceramic_api_key
export OPENAI_API_KEY=your_openai_api_key
```
### Install dependencies
```bash python theme={null}
pip install openai-agents ceramic_ai
```
```bash typescript theme={null}
npm install @openai/agents zod ceramic-ai
```
### Full example
```python python theme={null}
import os
from agents import Agent, Runner, function_tool
from ceramic_ai import Ceramic
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))
@function_tool
def ceramic_search(query: str) -> str:
"""Search the web using Ceramic.
Use for accurate current information — news, prices, recent events, documentation, general fact checking.
Returns up to 10 ranked results with titles, URLs, and descriptions.
Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.
Query rules:
- Queries must be 2-8 words
- Include specific entities, topics, locations, and dates
- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).
- Keep word order meaningful (`house cat` and `cat house` return different results)
- Good keyword query examples:
- "2026 Super Bowl halftime performer"
- "climate change effects global warming impact"
- "beginner investing strategies stocks bonds basics"
If the search returns no useful results, retry with a more specific keyword query.
Args:
query: keyword search query with 2–8 words
"""
results = ceramic.search(query=query)
return str(results)
agent = Agent(
name="Research assistant",
instructions="You have access to a web search tool. Use it to answer questions with up-to-date information.",
tools=[ceramic_search],
)
result = Runner.run_sync(agent, "What are the latest California tenant protection laws?")
print(result.final_output)
```
```typescript typescript theme={null}
import { Agent, run, tool } from "@openai/agents";
import { Ceramic } from "ceramic-ai";
import { z } from "zod";
const ceramic = new Ceramic({ apiKey: process.env.CERAMIC_API_KEY });
const TOOL_DESCRIPTION =
"Search the web using Ceramic.\n" +
"Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n" +
"Returns up to 10 ranked results with titles, URLs, and descriptions.\n" +
"Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n" +
"Query rules:\n" +
"- Queries must be 2-8 words\n" +
"- Include specific entities, topics, locations, and dates\n" +
"- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n" +
"- Keep word order meaningful (`house cat` and `cat house` return different results)\n" +
"- Good keyword query examples:\n" +
' - "2026 Super Bowl halftime performer"\n' +
' - "climate change effects global warming impact"\n' +
' - "beginner investing strategies stocks bonds basics"\n' +
"If the search returns no useful results, retry with a more specific keyword query.";
const ceramicSearchTool = tool({
name: "ceramic_search",
description: TOOL_DESCRIPTION,
parameters: z.object({
query: z.string().describe("keyword search query with 2–8 words"),
}),
async execute({ query }) {
const results = await ceramic.search({ query });
return JSON.stringify(results);
},
});
const agent = new Agent({
name: "Research assistant",
instructions:
"You have access to a web search tool. Use it to answer questions with up-to-date information.",
tools: [ceramicSearchTool],
});
const result = await run(agent, "What are the latest California tenant protection laws?");
console.log(result.finalOutput);
```
### Run the example
```bash python theme={null}
python agents_tool_calling.py
```
```bash typescript theme={null}
npx tsx agents_tool_calling.ts
```
# OpenClaw
Source: https://docs.ceramic.ai/integrations/openclaw
Add web-scale search to your OpenClaw agent using Ceramic
Ceramic integrates into OpenClaw as a web search provider. The tool calls Ceramic's MCP server and authenticates using an API key.
## Setup
Create a Ceramic account for free to get an API key.
```bash theme={null}
openclaw plugins install clawhub:@ceramicai/openclaw-ceramic-search
openclaw config set plugins.entries.ceramic-search.config.apiKey your_api_key_here
openclaw config set tools.web.search.provider ceramic
openclaw gateway restart
openclaw agent --agent main --message "What are the top AI news stories right now?"
```
A successful run will show Ceramic search results via citations in the agent response.
View plugin
View source code
# Vercel AI SDK
Source: https://docs.ceramic.ai/integrations/vercel
Use Ceramic Search within the Vercel AI SDK
We explain how to integrate Ceramic Search with the Vercel AI SDK to ground agent responses in high-quality web search results.
## Installation
```bash theme={null}
npm install @ceramicai/sdk ai @ai-sdk/openai
```
## API keys
Get your Ceramic API key and export it:
Create a Ceramic account for free to get an API key.
```
export CERAMIC_API_KEY=your_api_key
```
Also export any additional API keys you need, e.g., OpenAI:
```
export OPENAI_API_KEY=your_api_key
```
## Example usage
```typescript theme={null}
import { generateText, stepCountIs } from 'ai';
import { openai } from '@ai-sdk/openai';
import { webSearch } from '@ceramicai/sdk';
const { text } = await generateText({
model: openai('gpt-5.5'),
tools: {
webSearch: webSearch(),
},
stopWhen: stepCountIs(5),
prompt: 'What are the latest developments in AI?',
});
console.log(text);
```
Save the file as `example.ts`. In the same directory, create a `package.json` with:
```json theme={null}
{ "type": "module" }
```
Then run:
```bash theme={null}
npx tsx example.ts
```
## Configuration
```typescript theme={null}
webSearch({
apiKey: 'your_api_key', // defaults to process.env.CERAMIC_API_KEY
maxDescriptionLength: 3000, // 1000–8000, defaults to 3000
})
```
| Option | Type | Default | Description |
| ---------------------- | -------- | ----------------------------- | ------------------------------------------------- |
| `apiKey` | `string` | `process.env.CERAMIC_API_KEY` | Your Ceramic API key |
| `maxDescriptionLength` | `number` | `3000` | Max characters per result description (1000–8000) |
## Result shape
Each search call returns:
```typescript theme={null}
{
requestId: string;
results: Array<{
title: string;
url: string;
description: string;
}>;
totalResults: number;
executionTime: number; // seconds
}
```
View source code
View package
# Search MCP
Source: https://docs.ceramic.ai/mcp/ceramic-mcp
Connect AI agents to Ceramic Search via MCP
The Ceramic MCP Server enables AI systems to search the web through the [Model Context Protocol](https://modelcontextprotocol.io/).
## Setup
Click to install the Ceramic MCP server directly in Cursor.
Or add manually to your \~/.cursor/mcp.json:
```json theme={null}
{
"mcpServers": {
"ceramic": {
"url": "https://mcp.ceramic.ai/mcp"
}
}
}
```
Click to install the Ceramic MCP server directly in VS Code.
Or add manually to your .vscode/mcp.json:
```json theme={null}
{
"servers": {
"ceramic": {
"type": "http",
"url": "https://mcp.ceramic.ai/mcp"
}
}
}
```
1. Register the Ceramic plugin marketplace:
```bash theme={null}
claude plugin marketplace add CeramicTeam/ceramic-claude-code-plugins
```
2. Install the plugin:
```bash theme={null}
claude plugin install ceramic-search@ceramic-ai
```
Or within a session: `/plugin install ceramic-search@ceramic-ai`
3. Start a new Claude Code session. You'll be prompted to authenticate via WorkOS OAuth — a browser window opens automatically.
Connect directly from the **Claude desktop** app or **claude.ai**:
1. Go to **Settings → Connectors**
2. Click **Add Custom Connector**
3. Enter a name (e.g. `ceramic`)
4. Paste the MCP server URL: `https://mcp.ceramic.ai/mcp`
5. Click **Add**
Add to your Claude Desktop config file. Requires Node.js to be installed.
* macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
* Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json theme={null} theme={null}
{
"mcpServers": {
"ceramic": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.ceramic.ai/mcp"
]
}
}
}
```
Restart Claude Desktop after saving the config.
1. Register the Ceramic plugin marketplace:
```bash theme={null}
codex plugin marketplace add CeramicTeam/ceramic-codex-plugins
```
2. Install the plugin:
Inside a Codex session, run `/plugins`, find `ceramic-search` under Ceramic AI Plugins, and select install. Codex opens a WorkOS OAuth page automatically.
If the browser doesn't open automatically, run this from a terminal outside Codex:
```bash theme={null}
codex mcp login ceramic-search
```
3. Begin a new Codex session. The `ceramic-search` skill is active in every session and invokes automatically whenever the agent needs current web information.
**Note:** If your OAuth session expires, re-run the login command to re-authenticate.
Setup for additional clients can be found below.
**Note:** MCP server support requires a **ChatGPT Plus, Pro, or Team** plan.
From the ChatGPT desktop app or web browser:
1. Go to **Settings → Apps**
2. Click **Advanced Settings**
3. Enable **Developer Mode**
4. Click **Create App**
5. Paste the MCP server URL: `https://mcp.ceramic.ai/mcp`
6. Click **Add**
Add to your `~/.gemini/settings.json`:
```json theme={null}
{
"mcpServers": {
"ceramic": {
"httpUrl": "https://mcp.ceramic.ai/mcp"
}
}
}
```
1. In the Agent panel on the right side, click the three-dot menu
2. Select MCP Servers
3. Select **Manage MCP Servers**
4. Click **View Raw config**
5. Add the following:
```json theme={null}
{
"mcpServers": {
"ceramic": {
"serverUrl": "https://mcp.ceramic.ai/mcp"
}
}
}
```
Add to your `~/.hermes/config.yaml`:
```yaml theme={null}
mcp_servers:
ceramic:
url: "https://mcp.ceramic.ai/mcp"
headers:
Authorization: "Bearer YOUR_API_KEY"
```
Add to your `~/.kiro/settings/mcp.json`:
```json theme={null}
{
"mcpServers": {
"ceramic": {
"url": "https://mcp.ceramic.ai/mcp"
}
}
}
```
Add to your `opencode.json`:
```json theme={null}
{
"mcp": {
"ceramic": {
"type": "remote",
"url": "https://mcp.ceramic.ai/mcp",
"enabled": true
}
}
}
```
1. Go to **Settings → Agents → MCP Servers**
2. Click **+Add**
3. Add the following:
```json theme={null}
{
"ceramic": {
"serverUrl": "https://mcp.ceramic.ai/mcp"
}
}
```
Add to your `~/.codeium/windsurf/mcp_config.json`:
```json theme={null}
{
"mcpServers": {
"ceramic": {
"serverUrl": "https://mcp.ceramic.ai/mcp"
}
}
}
```
Open your Zed settings (**Zed → Settings → Open Settings JSON**) and add:
```json theme={null}
{
"context_servers": {
"ceramic": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.ceramic.ai/mcp"]
}
}
}
```
## Available Tools
### `ceramic_search`
Searches the web and returns a ranked list of results. Agents get better results with specific terms than with conversational phrasing. See [search best practices](/api/search/best-practices) for more guidance.
# Frequently Asked Questions
Source: https://docs.ceramic.ai/resources/faqs
## Search
Ceramic is a web search API designed for LLMs and AI agents. It returns relevant, high-quality content for keyword queries.
Traditional search APIs are built for human users clicking through results. Ceramic is purpose-built for AI applications. We built our own search index from the ground up, fully optimized to deliver high-quality, relevant content directly to LLMs and AI agents. At \$0.05 per 1,000 queries, Ceramic is up to 100x more affordable than traditional search APIs.
Ceramic currently supports English web pages. Support for additional languages is coming soon.
## Platform
Generate your API key on the **API keys** tab on the [Platform](https://platform.ceramic.ai). New accounts receive 1,000 free credits.
Monitor your API usage and remaining credits on the **Usage** tab on the [Platform](https://platform.ceramic.ai).
View our pricing plans on the [pricing page](https://ceramic.ai/pricing).
Invite team members and manage roles from the **Settings** tab on the [Platform](https://platform.ceramic.ai). See [Team Management](/admin/team-management) for details.
## Security & Compliance
Yes. Ceramic has completed SOC 2 Type II certification. Request access to our report through our [Trust Center](https://app.vanta.com/ceramicinc/trust/bjffwpxz7ln6fk65aigyuo).
No. Ceramic never uses your query data or results to train models. Your data remains yours.
Ceramic infrastructure is hosted in secure cloud environments with encryption at rest and in transit.
Report vulnerabilities to [info@ceramic.ai](mailto:info@ceramic.ai). We take all reports seriously and respond promptly.
# Python SDK
Source: https://docs.ceramic.ai/sdks/python-sdk
Official Python SDK for Ceramic
Create a Ceramic account for free to get an API key.
## Installation
```bash pip theme={null}
pip install ceramic_ai
```
## Search
```python theme={null}
from ceramic_ai import Ceramic
client = Ceramic(api_key="YOUR_API_KEY")
response = client.search(query="California rental laws")
print(response)
```
## Async Search
```python theme={null}
import asyncio
from ceramic_ai import AsyncCeramic
client = AsyncCeramic(api_key="YOUR_API_KEY")
async def main():
response = await client.search(query="California rental laws")
print(response)
asyncio.run(main())
```
### Parameters
| Parameter | Type | Description | Default |
| --------- | ------ | ----------------- | -------- |
| `query` | string | The search query. | Required |
View source code
View package
# TypeScript SDK
Source: https://docs.ceramic.ai/sdks/typescript-sdk
Official TypeScript SDK for Ceramic
Create a Ceramic account for free to get an API key.
## Installation
```bash npm theme={null}
npm install ceramic-ai
```
## Search
```typescript theme={null}
import { Ceramic } from "ceramic-ai";
const client = new Ceramic({ apiKey: "YOUR_API_KEY" });
const response = await client.search({ query: "California rental laws"});
console.log(response);
```
### Parameters
| Parameter | Type | Description | Default |
| --------- | ------ | ----------------- | -------- |
| `query` | string | The search query. | Required |
View source code
View package