# OptimAI Search Docs Full Reference This file is generated from the ordered documentation page tree. Each section contains the processed Markdown mirror for one page. ## OptimAI Search Docs Canonical URL: https://search-docs.pages.dev/docs/ Markdown URL: https://search-docs.pages.dev/docs.md # OptimAI Search Docs Build with decentralized live web search, clean URL scraping, and AI-agent retrieval workflows on the OptimAI network. OptimAI Search turns decentralized retrieval into data your agents can inspect, trust, and ship. Use these docs for API calls, SDK setup, examples, and AI-readable references. ## Start here * Read [Quick Start](/docs/quick-start) to make your first request. * Use [Search MCP](/docs/ai-agents/search-mcp) when an AI agent should call OptimAI Search as a tool. * Use the [x402 SDK](/docs/guides/x402-sdk) when your application needs paid HTTP-native search requests. * Use [BNB Agent](/docs/guides/bnb-agent) when a buyer needs an escrowed ERC-8183 job on BSC mainnet. * Review [API Overview](/docs/api/overview) before wiring production traffic. * Open [Markdown Access](/docs/ai-agents/markdown-access) if an agent needs clean text. ## Product surfaces OptimAI Search has two public web surfaces: | Surface | Domain | Purpose | | ---------- | ------------------------ | --------------------------------------------------- | | Search app | `search.optimai.network` | Human-facing live web search and scrape playground. | | Docs | `search-docs.pages.dev` | Developer docs, crawler pages, and AI references. | ## What is available now API-key clients can use Search, Scrape, URL Seeding, and Crawl. Search supports Standard mode and an explicit Agent mode for longer web, social, and crypto research. MCP access is available for agent tooling. The x402 SDK currently supports paid Search requests only; Scrape, Seed, and Crawl require API-key authentication. > Keep the docs useful for both humans and machines: every page should have a clear title, a short description, structured headings, and links to the next action. ## Quick Start Canonical URL: https://search-docs.pages.dev/docs/quick-start/ Markdown URL: https://search-docs.pages.dev/docs/quick-start.md # Quick Start Install the OptimAI tools, set an API key, and run a decentralized live web search request. This quick start gets a developer from zero to one working request. The API examples use server-side code because API keys should not be shipped to a browser. ## 1. Set your API key Create an API key at [search.optimai.network/api-keys](https://search.optimai.network/api-keys), then keep it outside source control. ```bash export OPTIMAI_API_KEY="sk_live_..." ``` ## 2. Run a copy-paste search Use `--fail-with-body` so a non-2xx response still prints the API error body. ```bash curl --fail-with-body https://api-onchain.optimai.network/external/v1/search \ -H "X-API-Key: $OPTIMAI_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: quick-start-search-001" \ -d '{ "query": "What are the latest AI agent retrieval patterns?" }' ``` The API returns `202 Accepted` with a search `id`. Poll the result until its status becomes terminal: ```bash curl --fail-with-body \ https://api-onchain.optimai.network/external/v1/search/SEARCH_ID \ -H "X-API-Key: $OPTIMAI_API_KEY" ``` ## 3. Call from TypeScript ```ts const response = await fetch("https://api-onchain.optimai.network/external/v1/search", { method: "POST", headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "", "Content-Type": "application/json", "Idempotency-Key": "quick-start-search-001", }, body: JSON.stringify({ query: "What changed in AI search infrastructure this week?", }), }) if (!response.ok) { const message = await response.text() throw new Error(`OptimAI Search failed: ${response.status} ${message}`) } const created = await response.json() console.log(`Search created: ${created.id} (${created.status})`) ``` Use `GET /external/v1/search/:id` to poll. A completed response stores the answer at `result.answer` and its evidence at `result.citations`. ## 4. Choose Search or Agent mode Standard Search is the default and costs 1 credit. For longer multi-step web, social, and crypto research, explicitly send `"search_mode": "agent"`; Agent mode costs 2 credits and accepts text queries only. ## 5. Choose the right integration | Use case | Start here | | ---------------------------------------------------------- | ---------------------------------------- | | A product backend needs live web answers | [First Query](/docs/guides/first-query) | | An AI coding agent should call OptimAI as a tool | [Search MCP](/docs/ai-agents/search-mcp) | | A request should be paid through HTTP-native x402 flow | [x402 SDK](/docs/guides/x402-sdk) | | A buyer needs escrow, verification, and onchain settlement | [BNB Agent](/docs/guides/bnb-agent) | ## 6. Read the response A completed Search response includes a cited answer under `result.answer` and source URLs under `result.citations`. ## Next step Continue with [First Query](/docs/guides/first-query) for response handling patterns. ## What Is OptimAI Search? Canonical URL: https://search-docs.pages.dev/docs/concepts/what-is-optimai-search/ Markdown URL: https://search-docs.pages.dev/docs/concepts/what-is-optimai-search.md # What Is OptimAI Search? A plain-language overview of decentralized live web Search, Scrape, URL Seeding, and Crawl. OptimAI Search is decentralized search infrastructure for AI agents. It gives applications a live web source layer instead of relying only on static model memory. ## Search Search lets an agent ask in natural language and get cited answers retrieved fresh from the live web. ## Scrape Scrape pulls clean structured data from any public URL: markdown, JSON, screenshots, on demand. ## URL Seeding URL Seeding discovers URLs from a starting URL without recursively crawling every page. ## Crawl Crawl recursively traverses a domain or subpath and extracts content from reachable pages within the requested limits. ## When to use each | Need | Capability | | ---------------------------------------------------------- | ----------- | | Ask in plain English and get cited live-web answers | Search | | Extract markdown, JSON, or screenshots from a known URL | Scrape | | Discover a curated starting set of URLs for agent research | URL Seeding | | Retrieve structured content across a domain or subpath | Crawl | ## Architecture Canonical URL: https://search-docs.pages.dev/docs/concepts/architecture/ Markdown URL: https://search-docs.pages.dev/docs/concepts/architecture.md # Architecture How a request routes through OptimAI API, live nodes worldwide, and structured results. OptimAI Search is designed around a simple flow: ask, distribute, retrieve, rank, synthesize. ## Request flow 1. Your app sends a natural language query or URL scrape request. 2. The OptimAI API validates authentication and request shape. 3. The request routes across live nodes worldwide. 4. Nodes fetch fresh sources from the live web. 5. OptimAI ranks results and stores a cited answer plus citations under the operation ID. ## Human and agent use Humans usually inspect the rendered docs and product UI. Agents usually need direct links, markdown-friendly content, and stable section headings. ## Deployment split The landing page and docs are separate apps: ```txt search-lp -> search.optimai.network search-docs -> search-docs.pages.dev ``` The standalone docs deployment is independently deployable from the Search app. Use the docs domain for developer references and AI-readable Markdown; use the Search app for the human-facing product and API playground. ## x402 Protocol Canonical URL: https://search-docs.pages.dev/docs/concepts/x402-protocol/ Markdown URL: https://search-docs.pages.dev/docs/concepts/x402-protocol.md # x402 Protocol How HTTP-native payments work for paid OptimAI Search requests. x402 is an open standard for HTTP-native payments. A client requests a resource, the server asks for payment with HTTP `402 Payment Required`, the client pays onchain, and the server delivers the resource. The model does not require accounts, API keys, or long-lived sessions. For OptimAI Search, the important idea is simple: the HTTP request itself can carry the payment proof. Your app asks for a search, receives a `402` payment challenge when payment is needed, signs the payment, and retries the same resource. ## Core concepts | Term | Meaning | | --------------- | ------------------------------------------------------------------- | | Resource | Any HTTP or HTTPS endpoint, such as an API, webpage, or RPC method. | | Client | The entity requesting and paying for the resource. | | Resource server | The HTTP server gating the resource behind payment. | | Facilitator | A server that verifies signatures and settles payments onchain. | | Scheme | Logical payment mechanism, such as `exact` for a fixed amount. | | Network | Specific blockchain, such as `eip155:8453` for Base mainnet. | ## Protocol flow OptimAI paid search flow: ```txt App request -> OptimAI Search API -> 402 payment challenge -> app signs payment -> paid retry -> decentralized search nodes -> cited answer ``` ```txt Client Resource Server Facilitator | | | |---- GET /resource ---------->| | | | | |<--- 402 + PAYMENT-REQUIRED --| | | base64 PaymentRequirements | | | | | client picks scheme and network, then signs payload | | | | |---- GET /resource ---------->| | | PAYMENT-SIGNATURE: ... | | | |---- POST /verify ------->| | |<--- verified ------------| | | | | |---- POST /settle ------->| | | |--> blockchain | |<--- Payment Execution ---| | | Response | |<--- 200 OK ------------------| | | PAYMENT-RESPONSE: ... | | ``` ## HTTP headers Server to client on the `402` response: ```http PAYMENT-REQUIRED: ``` Client to server on the paid retry: ```http PAYMENT-SIGNATURE: ``` Server to client on the successful response: ```http PAYMENT-RESPONSE: ``` ## Payment requirements The server sends a payment challenge shaped like: ```ts type PaymentRequirements = { scheme: string network: string maxAmountRequired: string resource: string description: string mimeType: string payTo: string maxTimeoutSeconds: number asset: string extra?: object } ``` ## Payment payload The client retries with a signed payment payload: ```ts type PaymentPayload = { scheme: string network: string payload: object } ``` ## Schemes `exact` is the common scheme for a fixed predetermined amount. It can use: * EIP-3009 `transferWithAuthorization` for USDC and EURC, which enables gasless payments * Permit2 for generic ERC-20 tokens, which may require a one-time approval ## Supported networks EVM support is available through `@x402/evm`. | Network | CAIP-2 | Mainnet | Testnet | | ------------ | -------------- | ------- | ------------- | | Base | `eip155:8453` | Yes | Base Sepolia | | Polygon | `eip155:137` | Yes | Not listed | | Arbitrum One | `eip155:42161` | Yes | Not listed | | World | `eip155:480` | Yes | World Sepolia | | Ethereum | `eip155:1` | Custom | Not listed | Solana support is available through `@x402/svm`. | Network | CAIP-2 | | ------- | ----------------------------------------- | | Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | | Devnet | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | ## Facilitators Payment verification and settlement are delegated to a facilitator. A facilitator validates the payment payload and submits settlement onchain. Common options: | Facilitator | Notes | | ---------------------------- | --------------------------------------------------------------------------- | | CDP Facilitator | Coinbase-hosted facilitator for Base, Polygon, Arbitrum, World, and Solana. | | x402.org Testnet Facilitator | Free testnet facilitator for Base Sepolia and Solana Devnet. | The facilitator interface is small: ```http POST /verify POST /settle ``` ## SDKs TypeScript: ```bash npm install @x402/core @x402/evm @x402/svm @x402/fetch ``` Python: ```bash pip install x402 ``` Go: ```bash go get github.com/x402-foundation/x402/go ``` For OptimAI-specific search flows, use the [`@optimai-network/x402-sdk`](/docs/guides/x402-sdk) guide. ## Installation Canonical URL: https://search-docs.pages.dev/docs/guides/installation/ Markdown URL: https://search-docs.pages.dev/docs/guides/installation.md # Installation Install the SDK or MCP server for OptimAI Search workflows. Use the direct SDK path for application code. Use the MCP path for coding assistants and agent runtimes. ## SDK ```bash pnpm add @optimai-network/x402-sdk ``` ## MCP server ```bash npx -y @optimai-network/search-mcp ``` ## Environment ```bash OPTIMAI_API_KEY=sk_live_... ``` Never commit API keys. For production, store them in the platform secret manager. For MCP-specific setup, continue with [Search MCP](/docs/ai-agents/search-mcp). For paid request flows, continue with [x402 SDK](/docs/guides/x402-sdk). ## First Query Canonical URL: https://search-docs.pages.dev/docs/guides/first-query/ Markdown URL: https://search-docs.pages.dev/docs/guides/first-query.md # First Query Send a natural language query and handle cited live-web output. Start with one specific question. Search creation is asynchronous: the first request returns an ID, then your client polls that ID for the final result. ```ts const response = await fetch("https://api-onchain.optimai.network/external/v1/search", { method: "POST", headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "", "Content-Type": "application/json", "Idempotency-Key": "first-query-001", }, body: JSON.stringify({ query: "What changed in AI agent retrieval this week?", }), }) if (!response.ok) { const message = await response.text() throw new Error(`OptimAI Search failed: ${response.status} ${message}`) } const created = await response.json() let search = created while (search.status !== "completed") { if (["failed", "cancelled"].includes(search.status)) { throw new Error(`Search ended with status ${search.status}`) } await new Promise((resolve) => setTimeout(resolve, 2_000)) const poll = await fetch( `https://api-onchain.optimai.network/external/v1/search/${created.id}`, { headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "" } }, ) if (!poll.ok) throw new Error(`Search polling failed: ${poll.status}`) search = await poll.json() } ``` ## Minimal response renderer ```ts console.log(search.result?.answer ?? "No answer returned") for (const source of search.result?.citations ?? []) { console.log(`- ${source.title ?? source.url}: ${source.url}`) } ``` ## What to inspect * `id`: the stable ID used for polling and support. * `status`: lifecycle state such as `pending`, `processing`, or `completed`. * `result.answer`: the cited live-web response when completed. * `result.citations`: URLs used to support the answer. ## Agent/Crypto mode Standard Search is the default and costs 1 credit. Send `search_mode: "agent"` for longer multi-step web, social, and crypto research; it costs 2 credits and only accepts text queries. Use a new idempotency key when changing modes. ## UI behavior Show sources close to the answer. The product promise is trust, so the evidence should not be hidden behind a secondary screen. Keep the original query and the source list together in logs. When a user asks "why did the answer say that?", the source URLs are the fastest path to debug the result. ## x402 SDK Canonical URL: https://search-docs.pages.dev/docs/guides/x402-sdk/ Markdown URL: https://search-docs.pages.dev/docs/guides/x402-sdk.md # x402 SDK Use the OptimAI x402 SDK to create paid searches and keep access across agents. [`@optimai-network/x402-sdk`](https://www.npmjs.com/package/@optimai-network/x402-sdk) is a typed client SDK for OptimAI x402 search endpoints. The current package requires Node.js 20.18.0 or newer. It wraps two awkward parts of the flow: * the initial `402 Payment Required` challenge * the follow-up `payment-signature` header generation It also returns a serializable `paymentContext` object, including the original `payment-signature`, so another agent or process can keep working with the same paid search later. ## Install ```bash pnpm add @optimai-network/x402-sdk ``` ## Quick start: EVM / Base ```ts import { createOptimaiX402Client, createViemPaymentHandler, } from "@optimai-network/x402-sdk" const paymentHandler = createViemPaymentHandler({ privateKey: process.env.X402_EVM_PAYER_PRIVATE_KEY!, rpcUrls: { "eip155:8453": "https://mainnet.base.org", "eip155:84532": "https://sepolia.base.org", }, }) const client = createOptimaiX402Client({ baseUrl: "https://api-onchain.optimai.network", paymentHandler, }) const { search, paymentContext } = await client.createSearch({ query: "What is liquidation?", search_mode: "agent", }) const completed = await client.waitForSearchCompletion(search.id, { paymentContext, }) console.log(completed.result?.answer) ``` ## Search modes and pricing Omit `search_mode` or set it to `"search"` for Standard Search. Set it explicitly to `"agent"` for longer multi-step web, social, and crypto research. Agent mode is text-only. The server advertises the exact payment amount in its `402 Payment Required` challenge. Every configured Agent-mode offer is exactly 2 times its Standard Search offer. Clients should always sign the amount from the challenge instead of hard-coding a price. Search mode is part of idempotency. Retrying an identical request with the same idempotency key is safe, but changing between Standard and Agent mode requires a new key. The x402 API currently supports Search only. Use API-key authentication for Scrape, Seed, and Crawl. `X402_EVM_PAYER_PRIVATE_KEY` is the payer wallet private key used to sign the x402 payment payload. It is not a server key, not a Coinbase facilitator key, and not an API key. ## Quick start: Solana ```ts import { createOptimaiX402Client, createSolanaPaymentHandler, } from "@optimai-network/x402-sdk" const paymentHandler = await createSolanaPaymentHandler({ privateKey: process.env.X402_SOLANA_PAYER_PRIVATE_KEY!, rpcUrls: { "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "https://api.mainnet-beta.solana.com", "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "https://api.devnet.solana.com", }, }) const client = createOptimaiX402Client({ baseUrl: "https://api-onchain.optimai.network", paymentHandler, }) const { search, paymentContext } = await client.createSearch({ query: "What is liquidation?", }) const completed = await client.waitForSearchCompletion(search.id, { paymentContext, }) console.log(completed.result?.answer) ``` `X402_SOLANA_PAYER_PRIVATE_KEY` is the Solana payer wallet private key used to sign the x402 payment payload. The payer wallet needs enough USDC for the payment and enough SOL to pay Solana network fees. ## Client methods `createOptimaiX402Client(config)` creates the high-level SDK client. Available methods: * `createSearch(input, options?)` * `getSearch(id, options?)` * `getSearchWithPaymentContext(id, options?)` (returns the latest Search and payment context) * `getSearchResult(id, options?)` (alias for `getSearchWithPaymentContext`) * `cancelSearch(id, options?)` * `waitForSearchCompletion(id, options?)` * `waitForSearchCompletionWithPaymentContext(id, options?)` * `rememberPaymentContext(context)` * `forgetPaymentContext(id)` ## Payment handlers `createViemPaymentHandler(config)` creates a payment handler backed by `@x402/evm`, `@x402/fetch`, and `viem`. Built-in network support: | CAIP-2 | Network | | -------------- | ------------ | | `eip155:8453` | Base | | `eip155:84532` | Base Sepolia | `createSolanaPaymentHandler(config)` creates a payment handler backed by the Solana x402 path. Built-in Solana network support: | CAIP-2 | Network | | ----------------------------------------- | -------------- | | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Solana mainnet | | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | Solana devnet | ## Payment requirements and `payTo` SDK users do not manually set `payTo`, amount, asset, network, or Solana `feePayer` values in normal usage. Those values come from the OptimAI server's `402 Payment Required` challenge. The SDK reads the server challenge, chooses the first supported payment option for the handler you configured, signs the payment, and retries the paid request. If your app needs to display the receiver or payment details, inspect `paymentContext.paymentRequired.accepts` after `createSearch()`. ## Payment statuses `x402_payment_status` in responses can be: | Status | Meaning | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `verified_unsettled` | Payment is verified, but settlement is not finalized in OptimAI yet. A chain transfer may already exist while the server is still confirming and persisting the settlement ID. | | `settlement_unconfirmed` | Legacy name accepted for compatibility with older server responses. Treat it like `verified_unsettled`. | | `settled` | Settlement is finalized and the settlement ID, usually a transaction hash, is persisted. | | `voided` | Payment was voided, usually because the request was cancelled or failed before settlement. | | `settlement_failed` | The server exhausted settlement retries and needs manual or operator follow-up. | ## Result-bound settlement The server settles payment only after Search completes with a usable, non-empty result. Failed, cancelled, timed-out, or empty-result searches are voided. Final result access uses a fresh result-bound authorization. Keep and pass the SDK's `paymentContext`; the SDK handles this follow-up proof without extending or reusing an expired initial authorization. When you need to persist access across workers, prefer the `WithPaymentContext` methods. They return both the Search response and the latest context, so you can store the rotated result-bound signature after a completed Search. A response can briefly be `completed` without a final result while payment is still `verified_unsettled` (or the legacy `settlement_unconfirmed`); continue polling with the returned context until the result is available or the operation is voided/failed. ## Persist access across agents `GET /external/v1/x402/search/:id` works best when the client replays the exact original `payment-signature` used for the paid create call. Because of that, the SDK returns: ```ts type SearchPaymentContext = { id: string paymentRequired: X402PaymentRequired paymentSignature?: string } ``` Persist this object alongside the search ID if another agent or process will poll or fetch the search later: ```ts const { search, paymentContext } = await client.createSearch({ query: "..." }) // Store paymentContext somewhere durable. client.rememberPaymentContext(paymentContext) const latest = await client.getSearch(search.id) ``` If you retry `createSearch()` with the same `Idempotency-Key` from another process, pass the previously stored context back in `options.existingPaymentContext` so the SDK can authenticate the `303` redirect target with the original signature: ```ts const retry = await client.createSearch( { query: "..." }, { idempotencyKey: "search-123", existingPaymentContext: paymentContext, } ) ``` ## Local smoke tests The package includes a smoke script for testing the built SDK against a live OptimAI x402 endpoint: ```bash X402_EVM_PAYER_PRIVATE_KEY=0x... pnpm smoke:local:evm ``` For backward compatibility, `pnpm smoke:local` still runs the EVM smoke path. Solana smoke testing is optional and additive: ```bash X402_SOLANA_PAYER_PRIVATE_KEY='[...]' pnpm smoke:local:solana ``` Defaults: * `OPTIMAI_X402_BASE_URL=https://api-onchain.optimai.network` * mainnet RPC for `eip155:8453` defaults to `https://mainnet.base.org` * Solana mainnet RPC defaults to `https://api.mainnet-beta.solana.com` Optional overrides: * `OPTIMAI_X402_QUERY="What is liquidation?"` * `OPTIMAI_X402_BASE_URL=http://localhost:3002` * `X402_EVM_BASE_RPC_URL=https://mainnet.base.org` * `X402_EVM_BASE_SEPOLIA_RPC_URL=https://sepolia.base.org` * `X402_SOLANA_MAINNET_RPC_URL=https://api.mainnet-beta.solana.com` * `X402_SOLANA_DEVNET_RPC_URL=https://api.devnet.solana.com` * `X402_PAYER_PRIVATE_KEY=0x...` for legacy EVM setups * `X402_RPC_URL_BASE=https://mainnet.base.org` * `X402_RPC_URL_BASE_SEPOLIA=https://sepolia.base.org` * `X402_RPC_URL=https://mainnet.base.org` * `OPTIMAI_X402_RUN_CANCEL=1` ## BNB Agent Canonical URL: https://search-docs.pages.dev/docs/guides/bnb-agent/ Markdown URL: https://search-docs.pages.dev/docs/guides/bnb-agent.md # BNB Agent Buy an OptimAI Search result through the ERC-8183 provider on BSC mainnet. BNB Agent is an escrowed onchain payment flow. It is separate from the HTTP API: the buyer creates and funds an ERC-8183 job, the provider runs OptimAI Search, and the buyer verifies the public deliverable before settlement. ## Before you start * Use a dedicated buyer wallet. Never use the provider wallet as the buyer. * Fund the wallet with BNB for BSC transaction gas and U for the job budget. * Keep the buyer private key out of source code, committed `.env` files, logs, prompts, and support chats. * Use a hardware wallet or encrypted keystore for real mainnet use. > **Mainnet warning:** this guide creates real BSC transactions. Verify every > network, address, policy, provider, and budget value before signing. ## Provider details | Item | Value | | ----------------- | ----------------------------------------------- | | Network | BSC mainnet, chain ID `56` | | Provider wallet | `0x0936686CBaF6fF410AFE042B297D299afB46bfb0` | | Commerce contract | `0xea4daa3100a767e86fded867729ae7446476eba6` | | Evaluator router | `0x51895229e12f9876011789b04f8698af06ccd6da` | | Optimistic policy | `0x9c01845705b3078aa2e8cff7520a6376fd766de5` | | Payment token | U: `0xcE24439F2D9C6a2289F741120FE202248B666666` | | Minimum budget | `0.001` U (`1000000000000000` base units) | U has 18 decimals. The budget is held in ERC-8183 escrow; it is not sent directly to the provider when the job is funded. ## Install the client Install the current BNBAgent client SDK in an isolated environment: ```bash python -m venv .venv . .venv/bin/activate pip install bnbagent requests ``` ## Create and fund a job The following example shows the ERC-8183 lifecycle. It uses a simple private key signer only to make the example readable. Replace it with your wallet or keystore integration in production. ```python import json import os import time from bnbagent.erc8183 import ERC8183Client from bnbagent.wallets import EVMWalletProvider PROVIDER = "0x0936686CBaF6fF410AFE042B297D299afB46bfb0" COMMERCE = "0xea4daa3100a767e86fded867729ae7446476eba6" ROUTER = "0x51895229e12f9876011789b04f8698af06ccd6da" POLICY = "0x9c01845705b3078aa2e8cff7520a6376fd766de5" BUDGET = 1_000_000_000_000_000 # 0.001 U wallet = EVMWalletProvider( password=os.environ["WALLET_PASSWORD"], private_key=os.environ["PRIVATE_KEY"], # Demo only: do not commit this. ) client = ERC8183Client(wallet, network="bsc-mainnet") # Refuse to sign if the SDK's current BSC preset differs from this provider. assert client.commerce.address.lower() == COMMERCE.lower() assert client.router.address.lower() == ROUTER.lower() assert client.policy.address.lower() == POLICY.lower() description = json.dumps( { "task": "optimai_search", "query": "What is OptimAI Search? Give a concise answer.", "options": {"timeout_seconds": 60}, }, separators=(",", ":"), ) # The job must remain open beyond the policy dispute window plus a buffer. expires_at = int(time.time()) + client.policy.dispute_window() + 86_400 created = client.create_job( provider=PROVIDER, expired_at=expires_at, description=description, ) job_id = created["jobId"] client.register_job(job_id, policy=POLICY) client.set_budget(job_id, BUDGET) # Use an exact approval for a one-job test. The default SDK floor can approve # a larger reusable allowance for convenience. client.fund(job_id, BUDGET, approve_floor=0) print(f"Funded job: {job_id}") ``` The transaction sequence is: 1. Create the job with the provider, expiry, and search description. 2. Register the OptimisticPolicy. 3. Set a budget of at least `0.001` U. 4. Approve the exact U amount for a one-job smoke test when needed. 5. Fund the job. Funding locks U in escrow and lets the provider begin work. Every write consumes BNB gas. Set the expiry beyond the live dispute window with a safe buffer. ## Describe the search Use a clear query with a timeframe, market, company, or domain when it matters. The provider accepts a structured description such as: ```json { "task": "optimai_search", "query": "Analyze the latest OptimAI ecosystem and crypto market news", "options": { "timeout_seconds": 60 } } ``` `query` is required and accepts up to 1,000 characters. `timeout_seconds` is optional and must be between 10 and 300. A plain-text description also works and is treated as the search query. `min_sources` is a rejection threshold, not a guarantee: if the completed Search has fewer citations than requested, the provider does not submit a successful ERC-8183 deliverable. The job then remains subject to ERC-8183 expiry/refund or dispute handling. Do not set `min_sources` for the first smoke test: a valid search can have zero citations. Do not put credentials or confidential information in the job description. ## Retrieve the result After the provider submits a result, retrieve the public manifest by job ID: ```bash curl --fail-with-body \ https://bnbagent.optimai.network/erc8183/job/{job_id}/response ``` The provider also exposes `GET /erc8183/health`. Read the job status, budget, and escrow state onchain with the ERC-8183 client or a BSC explorer. ## Provider route surface The provider application defines these routes: | Route | Intended use | | ------------------------------------ | --------------------------------------------------------------------- | | `GET /erc8183/job/{job_id}/response` | Buyer-facing public deliverable. | | `GET /erc8183/job/{job_id}/verify` | Check the deliverable hash when the route is exposed. | | `GET /erc8183/job/{job_id}` | Provider/operator job inspection. | | `GET /erc8183/health` | Liveness check. | | `GET /erc8183/ready` | Readiness check for funded-job discovery. | | `GET /erc8183/status` | Provider configuration and service status. | | `POST /erc8183/negotiate` | Provider negotiation protocol; normally operator/integration traffic. | A reverse proxy or hosted deployment may intentionally publish only the public response and health routes. Do not assume every route is internet-facing; the on-chain ERC-8183 job state remains authoritative for escrow and settlement. > **Results are public:** anyone can retrieve a V1 result. Never include > private queries, credentials, or other secrets in the job description. ## Verify the deliverable ERC-8183 stores the Keccak-256 hash of the canonical manifest onchain. Remove the transport-only `success` field, serialize the remaining object with sorted keys and compact separators, and compare its hash with `job.deliverable`: ```python import json import requests from web3 import Web3 payload = requests.get( f"https://bnbagent.optimai.network/erc8183/job/{job_id}/response", timeout=30, ).json() payload.pop("success", None) canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")) manifest_hash = Web3.keccak(text=canonical).hex() job = client.get_job(job_id) assert manifest_hash.lower() == Web3.to_hex(job.deliverable).lower() ``` ## Settlement and disputes After submission, payment remains in escrow during the OptimisticPolicy dispute window. A buyer may dispute during that period. If there is no valid rejection, settle only after the window ends: ```python client.settle(job_id) ``` Read the policy's live `dispute_window` and the job's `submitted_at` before settling. Do not settle immediately after receiving the manifest. ## Support checklist When reporting a problem, share only public information: * Job ID * BSC transaction hashes * Buyer public address * Public job status or error message Never share a seed phrase, private key, wallet password, or internal OptimAI API key. ## Production Setup Canonical URL: https://search-docs.pages.dev/docs/guides/production-setup/ Markdown URL: https://search-docs.pages.dev/docs/guides/production-setup.md # Production Setup Practical checks before running OptimAI Search in a production application. Production setup is mostly about predictable requests and clear failure handling. ## Checklist * Store API keys in secrets, not source code. * Set request timeouts. * Add retry logic only for retryable failures. * Log request IDs and status codes. * Keep source links in the user-facing result. * Cache stable scrape results when it makes product sense. ## Retry shape Use short retries with backoff for temporary network failures. Do not retry authentication errors without changing credentials. ## User messaging If a request fails, tell the user whether the issue is authentication, rate limit, upstream retrieval, or temporary service availability. ## Troubleshooting Canonical URL: https://search-docs.pages.dev/docs/guides/troubleshooting/ Markdown URL: https://search-docs.pages.dev/docs/guides/troubleshooting.md # Troubleshooting Common OptimAI Search setup and runtime issues. Most issues fall into three buckets: credentials, request shape, or web retrieval constraints. ## Authentication fails Check that `OPTIMAI_API_KEY` is present in the runtime environment and that the request includes the `X-API-Key` header. ```bash test -n "$OPTIMAI_API_KEY" && echo "key is set" ``` ```http X-API-Key: sk_live_... ``` ## Empty sources Make the query more specific. For scrape requests, confirm the URL is public and reachable without login, bot protection, or a private network. Try changing: ```json { "query": "latest OpenAI agent framework changes" } ``` to: ```json { "query": "OpenAI Agents SDK release notes and breaking changes this month" } ``` ## Slow response Set a product-specific timeout for each create or polling request. Agent mode can take longer than Standard Search because it performs multi-step research. ```ts const controller = new AbortController() const timeout = setTimeout(() => controller.abort(), 20_000) try { await fetch("https://api-onchain.optimai.network/external/v1/search", { method: "POST", signal: controller.signal, headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "", "Content-Type": "application/json", }, body: JSON.stringify({ query: "OptimAI Search" }), }) } finally { clearTimeout(timeout) } ``` ## 429 or 5xx responses Retry only with bounded backoff. Do not retry `400` or `401`; fix the request shape or credentials instead. ```ts const retryable = response.status === 429 || response.status >= 500 ``` ## Agent cannot read docs Send the agent to `/llms.txt` first, then to a specific markdown page such as `/docs/quick-start.md`. ## API Overview Canonical URL: https://search-docs.pages.dev/docs/api/overview/ Markdown URL: https://search-docs.pages.dev/docs/api/overview.md # API Overview Base URLs, request style, and response expectations for decentralized live web search. The API is designed for decentralized live web retrieval. Keep your client wrapper small: one base URL, one auth header, and explicit handling for non-2xx responses. ## Base URL ```txt https://api-onchain.optimai.network/external/v1 ``` ## Request format Use JSON request bodies and API key authentication. ```http X-API-Key: $OPTIMAI_API_KEY Content-Type: application/json ``` ## Search request ```http POST /search ``` ```json { "query": "What changed in AI search infrastructure this week?", "search_mode": "search" } ``` The create route returns `202 Accepted` with an ID. Poll `GET /search/:id`, or use `GET /search/:id/events` for Search events. Standard Search is the default and costs 1 credit. Explicit Agent mode (`"search_mode": "agent"`) costs 2 credits and accepts text queries only. ## Response shape Responses should be machine-readable and easy to render in a product UI. | Field | Meaning | | ------------------ | ------------------------------------------------------ | | `id` | Stable operation ID used for polling. | | `status` | Async lifecycle state. | | `result.answer` | Cited Search answer after completion. | | `result.citations` | Source URLs and labels for a completed Search. | | `result` | Operation-specific output for Scrape, Seed, and Crawl. | See [Response Format](/docs/api/response-format) for lifecycle statuses and a complete Search example. Review [Security](/docs/api/security) before putting keys or wallet-backed payment flows into production. ## Production handling * Treat `4xx` responses as request or credential issues. * Treat `429` and `5xx` responses as retryable only with bounded backoff. * Log the operation `id` for support and retries. * Keep API keys server-side; browser clients should call your backend. ## Authentication Canonical URL: https://search-docs.pages.dev/docs/api/authentication/ Markdown URL: https://search-docs.pages.dev/docs/api/authentication.md # Authentication Authenticate OptimAI Search API requests with an API key. Create or manage API keys at [search.optimai.network/api-keys](https://search.optimai.network/api-keys). Send API keys in the `X-API-Key` header. ```http X-API-Key: sk_live_... ``` `X-API-Key` is the recommended header. For compatibility, the backend also accepts `Authorization: Bearer sk_...`, but do not use both headers in one request. ## Key types | Prefix | Intended use | Important behavior | | ---------- | ------------------------------- | ---------------------------------------------------------------------------------------------- | | `sk_live_` | Normal production API access | Uses the normal production policy and does not expire by default. | | `sk_test_` | Active external-partner testing | Requires an active partner record, can expire, and may have lower rate and concurrency limits. | Test keys are a logical policy mode, not a separate sandbox. They use the same API infrastructure and account-level credit balance as live keys. Usage reports can still distinguish requests by API key. The backend checks the stored key type and partner status; changing only the visible prefix does not convert a live key into a test key. Scopes are assigned to each key. Give a key only the operation scopes the calling service needs (`search`, `scrape`, `seed`, or `crawl`, each with `create`, `read`, and `cancel` where applicable). ## Server-side only Keep keys on your server. Browser clients should call your backend, and your backend should call OptimAI Search. ```ts await fetch("https://api-onchain.optimai.network/external/v1/search", { method: "POST", headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "", "Content-Type": "application/json", }, body: JSON.stringify({ query: "What is OptimAI Search?" }), }) ``` ## Rotation Rotate keys when a team member leaves, a key is exposed, or an application boundary changes. ## Common mistakes * Sending `Authorization: Bearer ...` instead of `X-API-Key`. * Reading the key from a client-side `NEXT_PUBLIC_` or `VITE_` variable. * Forgetting to set the key in the production runtime after local testing. ## Endpoints Canonical URL: https://search-docs.pages.dev/docs/api/endpoints/ Markdown URL: https://search-docs.pages.dev/docs/api/endpoints.md # Endpoints Current asynchronous Search, Scrape, Seed, and Crawl API routes. All operation routes below are relative to: ```txt https://api-onchain.optimai.network/external/v1 ``` Create routes return `202 Accepted` with an operation `id`. Poll the matching `GET` route until the operation reaches a terminal status. ## Route overview | Operation | Create | Read | Cancel | Cost | | --------------- | ------------------------------------------ | ----------------- | -------------------- | ----------- | | Standard Search | `POST /search` | `GET /search/:id` | `DELETE /search/:id` | 1 credit | | Agent Search | `POST /search` with `search_mode: "agent"` | `GET /search/:id` | `DELETE /search/:id` | 2 credits | | Scrape | `POST /scrape` | `GET /scrape/:id` | `DELETE /scrape/:id` | 0.1 credits | | Seed | `POST /seed` | `GET /seed/:id` | `DELETE /seed/:id` | 1.5 credits | | Crawl | `POST /crawl` | `GET /crawl/:id` | `DELETE /crawl/:id` | 2 credits | API-key authentication supports all four operations. x402 currently supports Search only. ## Search Use Standard Search for a direct cited answer. Use explicit Agent mode for longer multi-step web, social, and crypto research. ### Search request fields | Field | Required | Description | | ------------- | ------------ | --------------------------------------------------------------------------------------------------- | | `query` | Yes | Natural-language input, 1–1,000 characters. | | `search_mode` | No | `"search"` (default) or `"agent"`. Agent mode is text-only. | | `input_mode` | No | `"text"` (default behavior) or the legacy `"url"` compatibility path. | | `search_url` | For URL mode | A public `http(s)` URL when `input_mode` is `"url"`. Use `/scrape` for new single-URL integrations. | The stable text-search flow only needs `query` and, when needed, an explicit `search_mode`. URL mode is retained for compatibility; Agent mode combined with URL input is rejected before work or billing starts. ```bash curl --fail-with-body https://api-onchain.optimai.network/external/v1/search \ -H "X-API-Key: $OPTIMAI_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: endpoint-search-001" \ -d '{ "query": "Analyze the latest OptimAI ecosystem and crypto market news", "search_mode": "agent" }' ``` `search_mode` defaults to `"search"`. Agent mode accepts text queries only; Agent mode combined with URL input returns `400 invalid_request` before work or billing starts. A completed response contains: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "query": "Analyze the latest OptimAI ecosystem and crypto market news", "result": { "answer": "...", "citations": [ { "id": 1, "url": "https://example.com/article", "title": "Example source", "snippet": "..." } ] } } ``` Search also supports `GET /search/:id/events` for Server-Sent Events and `DELETE /search/:id` for cancellation. The events endpoint streams `text/event-stream` updates while a Search is in progress. Event names include `search.started`, `search.progress`, `search.completed`, `search.warning`, `search.failed`, and `search.done`. Each event carries JSON data such as `stage`, `message`, `percent`, and partial source or answer fields. If the Search is already terminal, the events endpoint returns `409`; use `GET /search/:id` for the final response. ### List searches API-key clients can list their recent Search operations with: ```http GET /search?limit=20&offset=0&status=completed ``` Optional filters are `limit` (1–100, default 20), `offset` (0 or greater), `status`, `created_after`, and `created_before` (ISO 8601 timestamps). The response contains `data` items with `id`, `status`, `query`, timestamps, and `credits_charged`, plus `pagination` with `total`, `limit`, `offset`, and `has_more`. This list route is part of API-key access; the x402 Search path is operation-ID based. ## Scrape Use Scrape for one known public URL. ```http POST /scrape ``` ```json { "url": "https://example.com/article" } ``` Scrape supports `GET /scrape/:id` and `DELETE /scrape/:id`. ## Seed Use Seed to discover URLs from a starting URL without recursively extracting every page. ```http POST /seed ``` ```json { "seed_url": "https://example.com/docs", "max_urls": 100, "include_subdomains": false } ``` `max_urls` must be between 1 and 1000. Poll `GET /seed/:id` for the result. Use `DELETE /seed/:id` to cancel a pending or running operation when allowed. ## Crawl Use Crawl to traverse and extract pages from a site within explicit limits. ```http POST /crawl ``` ```json { "seed_url": "https://example.com/docs", "max_pages": 50, "max_depth": 2, "include_external": false, "allow_subdomains": false } ``` `max_pages` must be between 1 and 100; `max_depth` must be between 0 and 5. Poll `GET /crawl/:id` for the result. Use the matching `DELETE` route to cancel a pending or running operation when cancellation is still allowed. ## Idempotency Send a unique `Idempotency-Key` header with create requests. * The same key and request returns `303 See Other` with a `Location` header. * A changed body, route, or Search mode with the same key returns `409 Conflict`. * Omitted `search_mode` and explicit `"search"` are equivalent. * A new operation needs a new idempotency key. ## Response Format Canonical URL: https://search-docs.pages.dev/docs/api/response-format/ Markdown URL: https://search-docs.pages.dev/docs/api/response-format.md # Response Format Understand asynchronous operation statuses, progress, citations, and result fields. Search, Scrape, Seed, and Crawl are asynchronous. Create returns an operation ID, then the matching read route returns progress until a terminal state. ## Search statuses | Status | Meaning | | ------------- | -------------------------------------------------------------- | | `pending` | The operation was created but work has not started. | | `planning` | The retrieval strategy is being prepared. | | `searching` | Nodes are gathering relevant sources. | | `processing` | Sources are being processed and analyzed. | | `aggregating` | The final answer is being assembled. | | `completed` | Terminal success state with a result. | | `failed` | Terminal failure state; inspect the error fields when present. | | `cancelled` | Terminal cancellation state. | Other operation types may expose a smaller status set, but clients should always stop polling when the response reaches a terminal state. ## Search response example ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "query": "What is OptimAI Search?", "progress": { "stage": "completed", "percent": 100, "sources_found": 24, "sources_analyzed": 18, "sources_relevant": 7 }, "result": { "answer": "OptimAI Search is a decentralized Web3 search engine...", "summary": "A shorter answer summary.", "citations": [ { "id": 1, "url": "https://example.com/article", "title": "Example source", "snippet": "Supporting context..." } ] } } ``` ## Fields to keep * `id`: stable operation ID for polling, support, and idempotent retries. * `status`: lifecycle state used to decide whether to poll or finish. * `progress`: optional stage, percentage, and source counts while work runs. * `result.answer`: the completed Search answer. * `result.citations`: source URLs, titles, snippets, and related metadata. * `result`: operation-specific output for Scrape, Seed, and Crawl. * `error`: failure details when the operation fails or is rejected. Render the answer together with its citations. Keeping evidence visible makes it easier for users to check an important claim and for operators to debug a result. ## Scrape, Seed, and Crawl statuses Mining-style operations use a smaller lifecycle: | Status | Meaning | | ----------- | ---------------------------------------------------------------- | | `pending` | The job is accepted but work has not started. | | `running` | A worker is processing the job. | | `completed` | Terminal success with an operation-specific `result`. | | `failed` | Terminal failure; inspect the error response when present. | | `cancelled` | Terminal cancellation; reserved credits are refunded by the API. | ## Empty citations are valid For Search responses, `result.citations` is always an array, but it may be empty for a successful operation. Treat the answer and status as the success signal; do not assume an empty citation list means the transport failed. For x402 Search responses, `x402_payment_status` is present in addition to the normal Search fields. See [x402 SDK](/docs/guides/x402-sdk) for the payment status lifecycle. ## Errors Canonical URL: https://search-docs.pages.dev/docs/api/errors/ Markdown URL: https://search-docs.pages.dev/docs/api/errors.md # Errors Error categories and handling guidance for OptimAI Search API consumers. Handle errors by category, not only by message text. The server may include a machine-readable code, details, and request ID; preserve those fields for support and retry decisions. ## Error envelope ```json { "error": { "code": "invalid_request", "message": "The request body is invalid.", "details": {} }, "request_id": "req_123" } ``` The exact server `code` strings can evolve. Prefer the HTTP status, returned message, and top-level request ID instead of hard-coding an undocumented code enum. | Status | Meaning | Action | | ----------------------- | ------------------------------------- | ------------------------------------------------- | | `400` | Invalid request shape | Fix the payload. | | `401` | Missing or invalid key | Check authentication. | | `402` | Payment or credit required | Ask the user to add credits or payment. | | `404` | Operation not found or not accessible | Check the operation ID and API key. | | `429` | Rate limited | Back off and retry later. | | `500` | Server-side error | Preserve the request ID and retry only when safe. | | `502`/`503`/other `5xx` | Temporary service issue | Retry with backoff if safe. | ## Preserve context Log the status code, endpoint, and top-level `request_id` when available. Keep the request ID with the error message when opening a support ticket. ## Rate Limits Canonical URL: https://search-docs.pages.dev/docs/api/rate-limits/ Markdown URL: https://search-docs.pages.dev/docs/api/rate-limits.md # Rate Limits Throughput guidance for fair and stable OptimAI Search usage. Design clients to be steady rather than bursty. ## Client behavior * Keep concurrency bounded. * Retry only when the error is retryable. * Back off when the API returns `429`. * Avoid sending duplicate requests for the same user action. ## Known client limits These are validation limits in the MCP server and SDK, not a promise of an account quota: | Client setting | Allowed range | Default | | ----------------------- | ----------------------- | -------------------- | | MCP query length | 1–1,000 characters | — | | Blocking MCP timeout | 10–55 seconds | 45 seconds | | MCP poll interval | 500–10,000 milliseconds | 2,000 milliseconds | | MCP list page size | 1–100 records | 10 | | x402 completion timeout | — | 300,000 milliseconds | | x402 poll interval | — | 2,000 milliseconds | ## Product behavior Show the user that retrieval is in progress. If a request is delayed, keep source and status context visible instead of hiding the wait. Exact account quotas and partner policies are enforced by the backend and may vary by plan or environment. For create retries, use a unique idempotency key; reusing a key with a changed route, body, or Search mode returns a conflict. ## Security Canonical URL: https://search-docs.pages.dev/docs/api/security/ Markdown URL: https://search-docs.pages.dev/docs/api/security.md # Security Keep OptimAI API keys, payer wallets, and payment context scoped correctly. ## API keys * Keep `OPTIMAI_API_KEY` on a server or trusted agent host. * Send it in the `X-API-Key` header; do not put it in browser bundles, public URLs, prompts, or logs. * Rotate a key when a team member leaves, a key is exposed, or an application boundary changes. * Use separate keys for separate environments or application boundaries. A `sk_test_` key is a partner policy mode, not a standalone staging sandbox; it uses the same production infrastructure and account-level balance as a `sk_live_` key. * Test keys are only for active external partners and may expire or have lower rate and concurrency limits. Do not put a test key in a public client and do not treat its prefix as proof that it is isolated from production systems. ## x402 wallet keys and payment context * Treat `X402_EVM_PAYER_PRIVATE_KEY` and `X402_SOLANA_PAYER_PRIVATE_KEY` as real wallet secrets. * Keep payer keys in a secret manager or controlled local environment. They are not OptimAI API keys or Coinbase facilitator keys. * `paymentContext` can contain the original payment signature. Store it with the same care as a credential and pass it only to the worker that needs to poll or fetch the paid search. * Never hard-code `payTo`, amount, asset, or network values; sign the exact payment requirement returned by the server. ## BNB Agent wallets * Use a dedicated buyer wallet for BSC mainnet ERC-8183 jobs. * Never use the provider wallet as the buyer. * Keep the private key and wallet password out of source code, `.env` commits, logs, prompts, and support chats. * Verify chain ID `56`, provider, contracts, policy, token, and budget before signing a transaction. ## Content and result safety * Show citations so users can inspect source material. * Do not render untrusted source content as raw HTML. * Keep user metadata minimal when creating searches. * BNB Agent V1 result manifests are publicly retrievable. Never put confidential information in a job description. > Never share a seed phrase, private key, wallet password, or internal API key > with support or another agent. ## JavaScript Canonical URL: https://search-docs.pages.dev/docs/examples/javascript/ Markdown URL: https://search-docs.pages.dev/docs/examples/javascript.md # JavaScript A minimal JavaScript request to OptimAI Search. ```js const response = await fetch("https://api-onchain.optimai.network/external/v1/search", { method: "POST", headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "", "Content-Type": "application/json", "Idempotency-Key": "javascript-search-001", }, body: JSON.stringify({ query: "What are the latest AI search infrastructure patterns?", }), }) if (!response.ok) { throw new Error(`OptimAI Search failed: ${response.status} ${await response.text()}`) } const created = await response.json() console.log(`Poll /external/v1/search/${created.id}; current status: ${created.status}`) ``` ## TypeScript Canonical URL: https://search-docs.pages.dev/docs/examples/typescript/ Markdown URL: https://search-docs.pages.dev/docs/examples/typescript.md # TypeScript A typed helper for creating and polling OptimAI Search from server-side TypeScript. ```ts type SearchMode = "search" | "agent" type SearchRequest = { query: string search_mode?: SearchMode } type Search = { id: string status: string result?: { answer?: string citations?: Array<{ title?: string; url: string; snippet?: string }> } } const baseUrl = "https://api-onchain.optimai.network/external/v1" export async function searchOptimAI( input: SearchRequest, idempotencyKey: string, ): Promise { const apiKey = process.env.OPTIMAI_API_KEY if (!apiKey) throw new Error("OPTIMAI_API_KEY is required") const createdResponse = await fetch(`${baseUrl}/search`, { method: "POST", headers: { "X-API-Key": apiKey, "Content-Type": "application/json", "Idempotency-Key": idempotencyKey, }, body: JSON.stringify(input), }) if (!createdResponse.ok) { throw new Error( `Search creation failed: ${createdResponse.status} ${await createdResponse.text()}`, ) } const created = (await createdResponse.json()) as Search while (true) { const response = await fetch(`${baseUrl}/search/${created.id}`, { headers: { "X-API-Key": apiKey }, }) if (!response.ok) { throw new Error(`Search polling failed: ${response.status} ${await response.text()}`) } const search = (await response.json()) as Search if (search.status === "completed") return search if (["failed", "cancelled"].includes(search.status)) { throw new Error(`Search ended with status ${search.status}`) } await new Promise((resolve) => setTimeout(resolve, 2_000)) } } ``` Omit `search_mode` for Standard Search (1 credit), or set it to `"agent"` for longer multi-step web, social, and crypto research (2 credits). Agent mode is text-only. Use a different idempotency key when changing the request or mode. ## cURL Canonical URL: https://search-docs.pages.dev/docs/examples/curl/ Markdown URL: https://search-docs.pages.dev/docs/examples/curl.md # cURL A copyable cURL request for OptimAI Search. ```bash curl --fail-with-body https://api-onchain.optimai.network/external/v1/search \ -H "X-API-Key: $OPTIMAI_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: curl-search-001" \ -d '{ "query": "What are the latest AI search infrastructure patterns?" }' ``` Copy the returned `id`, then poll it: ```bash curl --fail-with-body \ https://api-onchain.optimai.network/external/v1/search/SEARCH_ID \ -H "X-API-Key: $OPTIMAI_API_KEY" | jq ``` Pipe to `jq` when debugging locally: ```bash curl --fail-with-body https://api-onchain.optimai.network/external/v1/search \ -H "X-API-Key: $OPTIMAI_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: curl-agent-search-001" \ -d '{"query":"Analyze OptimAI and current crypto market news","search_mode":"agent"}' | jq ``` Agent mode costs 2 credits, compared with 1 credit for Standard Search. It is text-only and must be selected explicitly. ## Simple Search Agent Canonical URL: https://search-docs.pages.dev/docs/examples/search-agent/ Markdown URL: https://search-docs.pages.dev/docs/examples/search-agent.md # Simple Search Agent Build a reliable start-and-poll research loop for an AI agent. Use a start-and-poll loop instead of one long blocking request. It fits agent host timeouts and keeps the search ID available for another worker. ## Agent policy ```text You are a research agent with OptimAI Search tools. When the user asks for current web or social information: 1. Start a search with the user's question. 2. Tell the user when the search is still running. 3. Poll the returned ID until it reaches a terminal state. 4. Answer from the completed result and include its cited sources. ``` ## Tool order 1. `POST /external/v1/search` with a natural-language query. 2. Poll `GET /external/v1/search/:id` until `completed`, `failed`, or `cancelled`. 3. Render `result.answer` together with `result.citations`. For longer multi-step web, social, or crypto research, send `"search_mode": "agent"`. Agent mode costs 2 credits and accepts text queries only. Use a new idempotency key when changing modes. ## Keep evidence visible Keep the original query, operation ID, status, and source list together in your logs. When a user asks why an answer was produced, the citation URLs are the fastest way to investigate it. ## Add Search to a Chatbot Canonical URL: https://search-docs.pages.dev/docs/examples/chatbot/ Markdown URL: https://search-docs.pages.dev/docs/examples/chatbot.md # Add Search to a Chatbot Give a chatbot a clear policy for when to search and how to handle results. Use OptimAI Search when an answer needs current web or social evidence. Do not search every conversational turn; search when freshness or citations matter. ## Search policy * Search for current events, prices, releases, policies, or questions that require sources. * Use the user's complete question as the query and add a timeframe or domain when it improves precision. * Keep the user informed while the asynchronous search is running. * Answer from the completed result and show the cited source links. * If retrieval fails, explain whether the problem was credentials, request shape, rate limiting, or temporary service availability. ## Server-side request Keep the API key in the chatbot backend: ```ts const response = await fetch( "https://api-onchain.optimai.network/external/v1/search", { method: "POST", headers: { "X-API-Key": process.env.OPTIMAI_API_KEY ?? "", "Content-Type": "application/json", "Idempotency-Key": `chat-${messageId}`, }, body: JSON.stringify({ query: userQuestion }), }, ) ``` Poll the returned ID, then pass `result.answer` and `result.citations` to the chat UI. Never expose `OPTIMAI_API_KEY` in a browser bundle. ## Pay-per-Call Search Canonical URL: https://search-docs.pages.dev/docs/examples/pay-per-call/ Markdown URL: https://search-docs.pages.dev/docs/examples/pay-per-call.md # Pay-per-Call Search Let an autonomous service pay for each Search request with x402. Use the [x402 SDK](/docs/guides/x402-sdk) when the caller should pay for each Search request with a wallet instead of using an OptimAI API key. ## Workflow 1. Create an x402 client with an EVM or Solana payer handler. 2. Call `createSearch()` without hard-coding a payment amount. 3. Let the SDK read the server's `402 Payment Required` challenge and sign its exact accepted requirement. 4. Persist the returned `paymentContext` with the search ID. 5. Poll with `waitForSearchCompletion()` or `getSearch()` using that context. 6. Retry a create request with the same idempotency key and `existingPaymentContext` if another worker needs to recover it. ## Agent mode Set `search_mode: "agent"` for longer multi-step web, social, and crypto research. Agent mode is text-only and the server advertises an offer exactly twice the Standard Search offer. Always sign the amount in the current 402 challenge. The x402 API currently supports Search only. Use API-key authentication for Scrape, Seed, and Crawl. ## Settlement safety The server settles only after a completed Search has a usable non-empty result. Failed, cancelled, timed-out, and empty-result searches are voided. Keep the fresh result-bound payment context available until the final result is read. ## LLMs.txt Canonical URL: https://search-docs.pages.dev/docs/ai-agents/llms-txt/ Markdown URL: https://search-docs.pages.dev/docs/ai-agents/llms-txt.md # LLMs.txt How AI agents should discover and consume OptimAI Search docs. The docs expose `/llms.txt` and `/llms-full.txt` so agents can discover the important pages without scraping navigation UI. ## Recommended agent flow 1. Read `/llms.txt`. 2. Open the most relevant markdown page. 3. Use the HTML docs only when visual context matters. ## Why this matters AI agents work best with stable headings, compact summaries, and clean internal links. The human docs should stay beautiful, but agents should not have to parse the visual shell. ## Markdown Access Canonical URL: https://search-docs.pages.dev/docs/ai-agents/markdown-access/ Markdown URL: https://search-docs.pages.dev/docs/ai-agents/markdown-access.md # Markdown Access Markdown-friendly docs paths for AI agents, crawlers, and source-of-truth snippets. Every docs page has a clean Markdown counterpart at the same path with `.md` added. The mirrors, indexes, aliases, and discovery headers are generated from the ordered `content/docs/**/*.mdx` page tree during every docs build. ## Discover the docs * [`/llms.txt`](/llms.txt) is the ordered index of every documentation page, including descriptions and direct Markdown links. * [`/llms-full.txt`](/llms-full.txt) contains every processed Markdown page in one file, with canonical URL boundaries. * [`/.well-known/llms.txt`](/.well-known/llms.txt) and [`/.well-known/llms-full.txt`](/.well-known/llms-full.txt) are equivalent discovery aliases. ## Markdown path pattern ```txt /docs/quick-start.md /docs/api/authentication.md /docs/examples/typescript.md ``` The pattern applies to every page in the documentation navigation tree, including the overview mirror at `/docs.md`. ## Content rules * Keep headings stable. * Prefer direct links over vague references. * Make examples copyable. * Keep roadmap placeholders clearly marked. ## Search MCP Canonical URL: https://search-docs.pages.dev/docs/ai-agents/search-mcp/ Markdown URL: https://search-docs.pages.dev/docs/ai-agents/search-mcp.md # Search MCP Connect OptimAI Search to Codex, Claude Desktop, Cursor, and other MCP-compatible AI hosts. `@optimai-network/search-mcp` is a Model Context Protocol server for the OptimAI External Search API. It lets an MCP-compatible AI host start searches, poll for results, list recent searches, and cancel running work. ## Tools | Tool | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `optimai_start_search` | Start a search and return the search ID immediately. Searches commonly take 60-90 seconds; call `optimai_get_search` with the ID to fetch progress and results. | | `optimai_search` | Convenience search that waits briefly for results. If the search is still running, it returns the search ID for `optimai_get_search`. | | `optimai_get_search` | Fetch current status or result of a past search by ID. | | `optimai_list_searches` | List recent searches, filterable by status and date. | | `optimai_cancel_search` | Cancel a running or pending search. | ## Setup Create or manage API keys at [search.optimai.network/api-keys](https://search.optimai.network/api-keys). ```bash export OPTIMAI_API_KEY="sk_live_..." ``` The MCP server reads `OPTIMAI_API_KEY` at startup. Never commit this key to source control. The published MCP server currently targets the production API base URL and uses Standard Search. It does not expose the direct API's `search_mode: "agent"` option. Use the [API](/docs/api/endpoints) or [x402 SDK](/docs/guides/x402-sdk) when an agent needs explicit Agent/Crypto mode or wallet-backed x402 payment. ## Codex CLI ```bash export OPTIMAI_API_KEY="sk_live_..." codex mcp add optimai-search \ --env OPTIMAI_API_KEY="$OPTIMAI_API_KEY" \ -- npx -y @optimai-network/search-mcp ``` Restart Codex, then run `/mcp` to confirm `optimai-search` is enabled. ## Claude Desktop and Cursor Use the same stdio server shape in Claude Desktop, Cursor, and other MCP hosts: ```json { "mcpServers": { "optimai-search": { "command": "npx", "args": ["-y", "@optimai-network/search-mcp"], "env": { "OPTIMAI_API_KEY": "sk-your-key-here" } } } } ``` Claude Desktop uses this format in `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or `%APPDATA%\Claude\claude_desktop_config.json` on Windows. Cursor can use the same format in `.cursor/mcp.json` in your project or in the global Cursor MCP config. ## Input limits * `query` must be 1 to 1,000 characters. * `optimai_search.timeout_seconds` accepts 10 to 55 seconds and defaults to 45. * `optimai_search.poll_interval_ms` accepts 500 to 10,000 milliseconds and defaults to 2,000. * `optimai_list_searches.limit` accepts 1 to 100 records and defaults to 10. `optimai_start_search` and `optimai_search` send the query to `POST /external/v1/search`; the MCP package does not currently let a caller override the API base URL. Use a direct API client when you need a different endpoint or environment. ## GitHub Copilot CLI Add the server interactively with `/mcp add`, or edit `~/.copilot/mcp-config.json`: ```json { "mcpServers": { "optimai-search": { "type": "local", "command": "npx", "args": ["-y", "@optimai-network/search-mcp"], "env": { "OPTIMAI_API_KEY": "sk-your-key-here" }, "tools": ["*"] } } } ``` ## GitHub Copilot cloud agent Add an environment secret or variable named `COPILOT_MCP_OPTIMAI_API_KEY`, then add this MCP configuration in the repository's Copilot cloud agent settings: ```json { "mcpServers": { "optimai-search": { "type": "local", "command": "npx", "args": ["-y", "@optimai-network/search-mcp"], "env": { "OPTIMAI_API_KEY": "$COPILOT_MCP_OPTIMAI_API_KEY" }, "tools": [ "optimai_start_search", "optimai_get_search", "optimai_list_searches" ] } } } ``` Use `tools: ["*"]` if you want to expose every tool, including `optimai_search` and `optimai_cancel_search`. ## Smoke test Use MCP Inspector to verify the server starts and exposes the expected schema: ```bash OPTIMAI_API_KEY=sk_live_... npx @modelcontextprotocol/inspector npx -y @optimai-network/search-mcp ``` ## Troubleshooting | Symptom | Check | | ------------------------------- | ------------------------------------------------------------------------------------------------------------ | | The server exits immediately | Confirm `OPTIMAI_API_KEY` is set in the host environment and restart the host. | | The host shows no OptimAI tools | Confirm the command is `npx -y @optimai-network/search-mcp`, then restart the host and inspect its MCP logs. | | A blocking search times out | Use `optimai_start_search` followed by `optimai_get_search`; long searches commonly take 60–90 seconds. | | A request is rejected | Check the query length, timeout, poll interval, and API-key permissions before retrying. | Use this safe host prompt when testing the integration: ```text You are a research agent with OptimAI Search tools. When the user asks for current web or social information, start a search, poll until it completes, then answer from the result and include its cited sources. ``` ## Recommended flow Use `optimai_start_search` for reliable agent workflows. It creates a search and returns immediately with an ID. Then call `optimai_get_search` to check progress and retrieve the completed answer. `optimai_search` is a convenience wrapper that starts a search and polls for a short time. It is useful for quick prompts, but long searches should use the start-and-poll flow. ## FAQ Canonical URL: https://search-docs.pages.dev/docs/reference/faq/ Markdown URL: https://search-docs.pages.dev/docs/reference/faq.md # FAQ Short answers for common OptimAI Search integration questions. ## Do users need a wallet to search? No. Normal API-key and MCP usage does not require a wallet. x402 callers use a wallet because the agent pays per request. BNB Agent callers use a buyer wallet because the job is an onchain ERC-8183 transaction flow. ## Do MCP hosts need an API key? Yes. The MCP server reads `OPTIMAI_API_KEY` and sends it to the External Search API. See [Search MCP](/docs/ai-agents/search-mcp). ## Does x402 need an OptimAI API key? No. The x402 SDK expects an EVM or Solana payer private key and signs the payment headers required by the server. ## Does BNB Agent need an OptimAI API key? No. The buyer funds an ERC-8183 job on BSC mainnet with U and pays transaction gas in BNB. See [BNB Agent](/docs/guides/bnb-agent). ## Are BNB Agent results private? No. V1 result manifests are publicly retrievable. Do not put confidential queries, credentials, or secrets in a job description. ## Why start and poll instead of waiting? Search can take longer than host tool timeouts. Starting first and polling by ID is more reliable and lets another worker continue the operation. ## Where do answers come from? OptimAI nodes gather sources from the open web and social platforms, then the AI agent synthesizes an answer with citations. ## Can another worker fetch a paid x402 Search? Yes, if it has the search ID and the original `paymentContext` returned by `createSearch()`. ## Changelog Canonical URL: https://search-docs.pages.dev/docs/reference/changelog/ Markdown URL: https://search-docs.pages.dev/docs/reference/changelog.md # Changelog Where to find current OptimAI Search SDK and documentation changes. This page records the current public integration surface. It intentionally avoids pinning an evergreen guide to one package version. ## Current integration surface * `@optimai-network/search-mcp` provides start, blocking search, get, list, and cancel tools for the OptimAI External Search API. * `@optimai-network/x402-sdk` provides typed paid Search flows, EVM and Solana payment handlers, payment-context persistence, idempotent retries, cancel, and completion helpers. * The API-key External API supports Search, Scrape, Seed, and Crawl. * Search supports Standard mode and explicit Agent mode; x402 currently covers Search only. * This docs site publishes Markdown mirrors plus `/llms.txt` and `/llms-full.txt` for agents. * The standalone docs deployment is available at `search-docs.pages.dev` and documents the current API-key, SSE, MCP, x402, and BNB Agent integration boundaries. ## Release source of truth Use the package's [npm release history](https://www.npmjs.com/package/@optimai-network/x402-sdk), the [SDK GitHub repository](https://github.com/OptimaiNetwork/optimai-x402-sdk), and the relevant package repository for exact versions and release notes.