# 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