# Quickstart Make your first search with Mux in under five minutes. Source: https://getmux.io/docs/quickstart Mux is one API and one bill for the modern search providers: Exa, Parallel, Keenable and DeepSeek. You send a single request format, pick a model such as `exa/auto` or `parallel/fast` (or let Mux choose), and get back one normalized response. If a provider fails, Mux can fall back to the next one automatically and you are only charged for the request that succeeds. ## 1. Get an API key 1. Sign in at [getmux.io/dashboard](https://getmux.io/dashboard) with Google. 2. Open [API keys](https://getmux.io/dashboard/keys) and create a key. 3. Copy it right away. The secret is shown once and stored only as a hash, so it can't be recovered later. API keys start with `mux-`. Put yours in an environment variable: ```bash export MUX_API_KEY="mux-..." ``` ## 2. Add credits Mux is prepaid. Searches draw down your credit balance, and a request with no balance left is rejected with `402 insufficient_credits`. Add credits from [Credits](https://getmux.io/dashboard/credits) in the dashboard (minimum $5). See [Pricing](https://getmux.io/docs/pricing) for the purchase fee. ## 3. Install an SDK TypeScript / JavaScript: ```bash npm install getmux ``` Python: ```bash uv add getmux ``` You can also call the HTTP API directly. The base URL is `https://api.getmux.io/v1`. ## 4. Run your first search ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "latest developments in fusion energy", "max_results": 5}' ``` ```ts import { Mux } from "getmux"; // Reads MUX_API_KEY from the environment, or pass the key explicitly: // const mux = new Mux({ apiKey: "mux-..." }); const mux = new Mux(); const res = await mux.search({ query: "latest developments in fusion energy", maxResults: 5, }); for (const r of res.results) { console.log(r.title, r.url); } console.log(`served by ${res.model} for $${res.usage.costUsd}`); ``` ```python from getmux import Mux # Reads MUX_API_KEY from the environment, or pass the key explicitly: # mux = Mux(api_key="mux-...") mux = Mux() res = mux.search("latest developments in fusion energy", max_results=5) for r in res.results: print(r.title, r.url) print(f"served by {res.model} for ${res.cost_usd}") ``` The SDKs read `MUX_API_KEY` by default. To use a key from somewhere else (a secrets manager, a per-customer lookup), pass it to the constructor: `new Mux({ apiKey })` in TypeScript or `Mux(api_key=...)` in Python. An explicit key always wins over the environment variable, so one process can hold several clients with different keys. With no `model`, Mux uses `auto`: it tries `parallel/fast` first, then `exa/auto`, then `keenable/pro`, and returns the first one that succeeds. See [Models](https://getmux.io/docs/models) to pin a specific model or set your own fallback order. ## 5. Read the response ```json { "id": "req_9fVx2kQ0b1c3dEfG", "model": "parallel/fast", "provider": "parallel", "query": "latest developments in fusion energy", "results": [ { "title": "Fusion milestone announced", "url": "https://example.com/fusion-milestone", "snippet": "Researchers reported … sustained plasma for …", "content": null, "published_date": "2026-09-14" } ], "usage": { "cost_usd": 0.001, "results": 5, "latency_ms": 742, "byok": false }, "attempts": [{ "model": "parallel/fast", "source": "platform", "status": "ok" }] } ``` - `model` and `provider` tell you which provider actually answered. When fallbacks are involved this can differ from the model you tried first. - `results[].snippet` holds the relevant excerpts. `content` is only filled when you ask for `content: "full"` and the provider supports it. - `usage.cost_usd` is what this request cost. Failed attempts that were retried on another model are refunded and don't appear in the cost. - `attempts` lists every model Mux tried, in order, with its outcome. The full field reference is on the [Search API](https://getmux.io/docs/search) page. ## Next steps - [Authentication](https://getmux.io/docs/authentication): API keys, management keys and key security. - [Models](https://getmux.io/docs/models): choose a provider, set fallbacks, and filter by domain or date. - [Keys](https://getmux.io/docs/keys): create one key per agent or customer, each with its own spend limit. - [Bring your own keys](https://getmux.io/docs/byok): use your own Exa, Parallel, Keenable or DeepSeek account. - [SDKs](https://getmux.io/docs/sdks): every method in the TypeScript and Python clients. --- # Authentication How Mux API keys and management keys work, and how to keep them safe. Source: https://getmux.io/docs/authentication Every request is authenticated with a bearer token in the `Authorization` header: ```bash curl https://api.getmux.io/v1/credits \ -H "Authorization: Bearer $MUX_API_KEY" ``` ```ts import { Mux } from "getmux"; const mux = new Mux({ apiKey: process.env.MUX_API_KEY }); console.log(await mux.credits()); ``` ```python from getmux import Mux mux = Mux(api_key="mux-...") # or set MUX_API_KEY print(mux.credits()) ``` The token must start with `mux-`. A missing, malformed or unknown token returns `401 invalid_api_key`. Two endpoints are public and need no key: `GET /v1/models` and `GET /v1/credits/pricing`. ## Two kinds of key | | API key | Management key | |---|---|---| | Prefix | `mux-` | `mux-mgmt-` | | Can call `POST /v1/search` | Yes | **No** | | Can create, edit and delete API keys | No | Yes | | Can manage [provider keys](https://getmux.io/docs/byok) | No | Yes | | Can read credits, usage and its own key (`GET /v1/key`) | Yes | Yes | Use an **API key** in your application or agent. Use a **management key** in the tooling that issues keys to your customers or agents. Keeping the two separate means a leaked search key can't be used to mint new keys or change billing settings, and a management key can't be used to run up search costs. Calling an endpoint with the wrong kind of key returns `403 wrong_key_type`: ```json { "error": { "code": "wrong_key_type", "message": "Management keys can't make search calls. Use an API key." } } ``` Both kinds of key belong to your account and draw on the same credit balance. ## Creating keys API keys are created in the dashboard under [API keys](https://getmux.io/dashboard/keys), or programmatically with a management key through [`POST /v1/keys`](https://getmux.io/docs/keys). Management keys (`mux-mgmt-...`) are created from the same page of the dashboard. A key's secret is **shown exactly once**, when it is created. Mux stores only a SHA-256 hash of it, so we can't show it to you again. If you lose it, create a new key and delete the old one. In listings you'll only see a masked label like `mux-AbCdE…`. ## Per-key limits, disabling and expiry Each API key can have its own: - **Credit limit** in USD, optionally resetting daily, weekly or monthly. - **Expiry** time, after which the key stops working. - **Disabled** switch, which turns a key off immediately without deleting it. These are enforced on the server. A request that would exceed a key's limit is rejected with `402 key_limit_exceeded` and costs nothing. A disabled key returns `401 key_disabled`, and an expired key returns `401 key_expired`. See [Keys](https://getmux.io/docs/keys) for details. > **Note:** Changes you make to a key (disabling it, deleting it, changing a limit) apply right away on the server that handles your request and can take up to about 15 seconds to propagate across all servers. ## Keeping keys safe - **Use environment variables.** Don't commit keys to source control or ship them in browser or mobile code. The SDKs read `MUX_API_KEY` automatically. - **Issue one key per agent, service or customer.** Per-key usage is visible in the dashboard, and one compromised key can be deleted without disturbing the rest. - **Set a credit limit on every key.** A runaway loop in an agent is the usual way a balance gets drained. A limit caps the damage to that key. - **Set an expiry** on keys you hand to short-lived jobs or external users. - **Rotate by replacing.** Create the new key, deploy it, then delete the old one. - **Delete leaked keys immediately** from the dashboard or with `DELETE /v1/keys/{id}`. --- # Models and routing Choose a provider, set fallbacks, and understand how Mux normalizes Exa, Parallel, Keenable and DeepSeek. Source: https://getmux.io/docs/models A Mux **model** is a provider and one of its search modes, written `provider/variant`: for example `exa/auto`, `parallel/fast` or `keenable/pro`. The same request body works with every model. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | The table above is loaded live from `GET /v1/models`. Prices are per 1,000 requests and include 10 results. Prices are current list prices and can change. You can fetch the same data yourself: ```bash curl https://api.getmux.io/v1/models ``` ```ts const models = await mux.models(); ``` ```python models = mux.models() ``` Each entry has `id`, `provider`, `description`, `pricing` (`per_1k_requests_usd`, `included_results`, `per_1k_extra_result_usd`), `max_results` and `typical_latency_ms`. ## Picking a model Pass `model` to use one specific model: ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "rust async runtimes compared", "model": "exa/deep"}' ``` ```ts const res = await mux.search({ query: "rust async runtimes compared", model: "exa/deep" }); ``` ```python res = mux.search("rust async runtimes compared", model="exa/deep") ``` An unknown model id returns `400 unknown_model`. If you pin one model and it fails, the request fails (after refunding you); there is no silent substitution. ## `auto` If you omit `model`, or pass `"auto"`, Mux tries these in order and returns the first success: 1. `parallel/fast` 2. `exa/auto` 3. `keenable/pro` `auto` is a good default when you care about getting an answer more than about which provider gives it. ## Fallback lists Pass `models` (up to 5) to set your own order. Mux tries each in turn until one succeeds: ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "sec filings for acme corp", "models": ["exa/auto", "parallel/basic", "keenable/pro"]}' ``` ```ts const res = await mux.search({ query: "sec filings for acme corp", models: ["exa/auto", "parallel/basic", "keenable/pro"], }); ``` ```python res = mux.search( "sec filings for acme corp", models=["exa/auto", "parallel/basic", "keenable/pro"], ) ``` When `models` is set it takes precedence over `model`. The `attempts` array in the response shows what was tried: ```json "attempts": [ { "model": "exa/auto", "source": "platform", "status": "error", "error": "exa 503: ..." }, { "model": "parallel/basic", "source": "platform", "status": "ok" } ] ``` ### What fallbacks cost You pay for the model that succeeds. Mux reserves the price of each attempt before calling the provider and refunds it if that call fails or is skipped. A model is *skipped* (not called, not charged) when its price would exceed your balance or the API key's credit limit, so a cheaper model later in the list can still serve the request. The one exception is a **timeout**. If a provider hasn't answered after 15 seconds, Mux gives up and moves on, but the provider has almost always finished the search by then and billed for it. That attempt therefore keeps the model's base price (the price for up to 10 results, never the extra-results portion), and the amount is shown as `cost_usd` on its entry in `attempts`. Timeouts are rare on every model except `exa/deep` under heavy load. If every attempt fails, you get `502 all_providers_failed` with the `attempts` array. Your balance is unchanged unless one of the attempts timed out. See [Errors](https://getmux.io/docs/errors). ## How providers are normalized Mux translates your request into each provider's native format and translates the answer back into one shape. Differences you should know about: | Feature | Exa | Parallel | Keenable | DeepSeek | |---|---|---|---|---| | Max `max_results` | 100 | 50 | 50 | 50 | | Max `query` length | 2,000 | 2,000 | 2,000 | 128 | | `include_domains` / `exclude_domains` | Native | Native | One domain native (as `site`); more handled by Mux | Native as `site:` operators when they fit in 128 characters; otherwise handled by Mux | | `published_after` | Native | Native | Native | Native (`after:` operator) | | `published_before` | Native | Not applied upstream | Native | Native (`before:` operator) | | `content: "full"` | Full page text | Excerpts joined together | Not available (`content` is `null`) | Full page text | | Snippet | Highlights | Excerpts | Provider snippet | Long model-oriented summary (often 1,000+ characters) | Further details: - **Domain filters are always enforced.** In addition to passing them upstream where supported, Mux filters the returned results by `include_domains` and `exclude_domains` (matching the domain and its subdomains, ignoring a leading `www.`). Because filtering happens after the provider answers, you may get fewer results than `max_results`, and the price is based on what the provider returned rather than on what survived the filter (see [Pricing](https://getmux.io/docs/pricing)). - **Keenable** accepts a single `site`. If you pass exactly one include domain it is sent natively; with several, Mux filters the results itself. - **DeepSeek** has no filter fields; Mux appends your dates (`after:`, `before:`) and domains (`site:`, `-site:`) to the query as operators. The whole query is limited to 128 characters, so when the operators don't fit Mux drops the domain operators first, then the date operators, and filters the results itself instead. A query that is itself over 128 characters fails that attempt (not charged) and the request moves to the next model in your list. Dates are always re-checked by Mux for every provider. - **`published_before` and Parallel.** Parallel's search has no upper date bound, so `published_before` is not applied when a Parallel model serves the request. Use an Exa, Keenable or DeepSeek model if you need it. - **`max_results` is capped to the model's maximum.** Asking for 80 results from a Parallel model returns at most 50, and you are billed for the 50. - **Dates** in results are normalized to `YYYY-MM-DD`; `published_date` is `null` when the provider doesn't report one. ## Snippets and full content `content` controls how much text comes back: - `"snippet"` (default): short, relevant excerpts in `snippet`. - `"full"`: also fills `content` with longer text where the provider supports it. See the table above for what each provider returns. ## `provider_options` `provider_options` is an escape hatch for provider-specific features Mux doesn't model. Only the options below are accepted; each is added to the upstream request for its provider and ignored for any other provider in your `models` list. Anything that changes what you'd be billed for (the query, search tier, result count, content and crawl options) or that Mux uses for filtering (domains, dates) can't be set here, and a request that tries gets `400 invalid_request`. | Provider | Allowed options | |---|---| | Exa | `category`, `userLocation`, `moderation`, `includeText`, `excludeText` | | Parallel | `max_chars_total`, `client_model`, `advanced_settings.excerpt_settings`, `advanced_settings.location` | | Keenable | none yet | | DeepSeek | `ori_question` (the user's original question, to improve query understanding), `localization` (an IP address to localize results) | Values are passed to the provider as given; see the [Exa](https://exa.ai/docs/reference/search), [Parallel](https://docs.parallel.ai/api-reference/search-api/search) and [DeepSeek](https://api-docs.deepseek.com/guides/search/) references for what each accepts. The whole object must stay under 8 KB. ```ts const res = await mux.search({ query: "transformer architecture survey", model: "exa/auto", providerOptions: { category: "research paper" }, }); ``` ```python res = mux.search( "transformer architecture survey", model="exa/auto", provider_options={"category": "research paper"}, ) ``` > **Note:** option *values* are not validated by Mux. A value the provider rejects fails that attempt (and is refunded) like any other provider error, and the request moves on to the next model in your list, if any. If you need an option that isn't listed, email support@getmux.io. --- # Search Reference for POST /v1/search, the unified search endpoint. Source: https://getmux.io/docs/search `POST https://api.getmux.io/v1/search` Runs a search through one or more providers and returns normalized results. Requires an [API key](https://getmux.io/docs/authentication) (`mux-...`). Management keys get `403 wrong_key_type`. ## Request body | Field | Type | Default | Description | |---|---|---|---| | `query` | string | required | The search query, 1 to 2,000 characters. DeepSeek accepts at most 128; a longer query fails that attempt (unbilled) and moves to the next model. | | `model` | string | `"auto"` | A model id such as `exa/auto`, or `"auto"`. See [Models](https://getmux.io/docs/models). | | `models` | string[] | none | Fallback order, at most 5 model ids. Takes precedence over `model`. | | `max_results` | integer | `10` | Number of results, 1 to 100. Capped to the model's own maximum (100 for Exa, 50 for Parallel, Keenable and DeepSeek). | | `include_domains` | string[] | none | Only return results from these domains (subdomains included). | | `exclude_domains` | string[] | none | Drop results from these domains. | | `published_after` | string | none | Only results published on or after this date, `YYYY-MM-DD`. | | `published_before` | string | none | Only results published on or before this date, `YYYY-MM-DD`. Not applied by Parallel models. | | `content` | string | `"snippet"` | `"snippet"` or `"full"`. | | `provider_options` | object | none | Allow-listed provider-specific options added to the upstream request. See [Models](https://getmux.io/docs/models). | Unknown fields are rejected with `400 invalid_request`, and so are values outside the ranges above. The TypeScript SDK uses camelCase for these (`maxResults`, `includeDomains`, `excludeDomains`, `publishedAfter`, `publishedBefore`, `providerOptions`); the Python SDK uses the snake_case names shown here. ## Response ```json { "id": "req_9fVx2kQ0b1c3dEfG", "model": "exa/auto", "provider": "exa", "query": "open source vector databases", "results": [ { "title": "A guide to open source vector databases", "url": "https://example.com/vector-dbs", "snippet": "Milvus, Qdrant and Weaviate … compared on recall and cost", "content": null, "published_date": "2026-05-02" } ], "usage": { "cost_usd": 0.007, "results": 1, "latency_ms": 1180, "byok": false }, "attempts": [{ "model": "exa/auto", "source": "platform", "status": "ok" }] } ``` | Field | Description | |---|---| | `id` | Request id (`req_...`). Also returned in the `x-mux-request-id` header. | | `model` | The model that served the request. Also in the `x-mux-model` header. | | `provider` | `exa`, `parallel`, `keenable` or `deepseek`. | | `query` | Your query, echoed back. | | `results[].title` | Page title, or `null`. | | `results[].url` | Result URL. | | `results[].snippet` | Relevant excerpt(s), or `null`. | | `results[].content` | Longer text when `content: "full"` and supported, otherwise `null`. | | `results[].published_date` | `YYYY-MM-DD`, or `null` if unknown. | | `usage.cost_usd` | What this request cost. `0` for a free [BYOK](https://getmux.io/docs/byok) request. | | `usage.results` | Number of results returned (after domain filtering). | | `usage.latency_ms` | Time spent in the provider call that succeeded. | | `usage.byok` | `true` if your own provider key served the request. | | `attempts` | Every model tried, in order. See below. | ### `attempts` Each entry has: - `model`: the model id. - `source`: `"platform"` (Mux's provider account) or `"byok"` (your own key). May be absent when a model was skipped because its provider is not available. - `status`: `"ok"`, `"error"` (the provider call failed and was refunded) or `"skipped"` (not called, for example because of insufficient credits or a key limit). - `error`: present for `error` and `skipped`, with the reason. - `cost_usd`: present only on an `error` attempt that timed out, which keeps the model's base price. See [Pricing](https://getmux.io/docs/pricing). The last entry is the one that succeeded. Upstream calls time out after 15 seconds, and a timeout counts as a failed attempt. ### Response headers | Header | Value | |---|---| | `x-mux-request-id` | Same as `id`. Include it when contacting support. | | `x-mux-model` | Same as `model`. | ## Examples ### Filter by domain and date ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "gpu supply chain constraints", "model": "exa/auto", "max_results": 10, "include_domains": ["reuters.com", "ft.com"], "published_after": "2026-01-01" }' ``` ```ts const res = await mux.search({ query: "gpu supply chain constraints", model: "exa/auto", maxResults: 10, includeDomains: ["reuters.com", "ft.com"], publishedAfter: "2026-01-01", }); ``` ```python res = mux.search( "gpu supply chain constraints", model="exa/auto", max_results=10, include_domains=["reuters.com", "ft.com"], published_after="2026-01-01", ) ``` ### Exclude noisy sources ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "best static site generators", "exclude_domains": ["pinterest.com", "quora.com"]}' ``` ```ts const res = await mux.search({ query: "best static site generators", excludeDomains: ["pinterest.com", "quora.com"], }); ``` ```python res = mux.search( "best static site generators", exclude_domains=["pinterest.com", "quora.com"], ) ``` ### Fallbacks across providers ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "postgres connection pooling", "models": ["exa/fast", "parallel/fast", "keenable/pro"]}' ``` ```ts const res = await mux.search({ query: "postgres connection pooling", models: ["exa/fast", "parallel/fast", "keenable/pro"], }); console.log(res.model, res.attempts); ``` ```python res = mux.search( "postgres connection pooling", models=["exa/fast", "parallel/fast", "keenable/pro"], ) print(res.model, res.attempts) ``` ## Billing for a request The price is the model's list price for the number of results the provider returned (capped at the number requested and the model's maximum). Extra results beyond the first 10 cost extra on Exa and Parallel. The amount is reserved before the provider is called and refunded if the call fails, except that a timed-out attempt keeps the base price. A request that is rejected, skipped or refunded costs nothing. See [Pricing](https://getmux.io/docs/pricing). ## Errors | Status | Code | When | |---|---|---| | 400 | `invalid_request` | Bad JSON, unknown field, or a value out of range. | | 400 | `unknown_model` | A model id isn't recognized. | | 401 | `invalid_api_key`, `key_disabled`, `key_expired` | Authentication failed. | | 402 | `insufficient_credits` | Not enough credits for any attempted model. | | 402 | `key_limit_exceeded` | The API key's credit limit would be exceeded. | | 403 | `wrong_key_type` | You used a management key. | | 502 | `all_providers_failed` | Every attempt failed. Includes `attempts`. | The full list is on the [Errors](https://getmux.io/docs/errors) page. --- # Credits and usage Check your balance, add credits, and read your ledger, usage and request activity. Source: https://getmux.io/docs/credits Mux is prepaid. Your account has one credit balance in US dollars, shared by all of your keys. These endpoints let you read it, add to it, and see where it went. The [dashboard](https://getmux.io/dashboard) shows the same data: your balance on the overview page, spend over time and by model and key under usage, recent requests under activity, and purchases and grants under billing. These endpoints always return data for the account the key belongs to. What a key may see depends on its kind: a **management key** sees the whole account; a search-only **API key** sees its own balance and key info, and only *its own* requests in usage and activity. The ledger and checkout need a management key. That way a key you hand to one agent can't read what the rest of the account is doing or open a purchase in your name. ## Get your balance `GET /v1/credits` ```bash curl https://api.getmux.io/v1/credits \ -H "Authorization: Bearer $MUX_API_KEY" ``` ```ts const balanceUsd = await mux.credits(); // number ``` ```python balance_usd = mux.credits() # float ``` ```json { "balance_usd": 12.5 } ``` ## Credit pricing `GET /v1/credits/pricing` Public (no key needed). Returns the current purchase fee and the BYOK terms. ```bash curl https://api.getmux.io/v1/credits/pricing ``` ```json { "fee_rate": 0.055, "fee_min_usd": 0.8, "min_purchase_usd": 5.0, "byok": { "free_requests_per_month": 1000000, "fee_rate": 0.05 } } ``` See [Pricing](https://getmux.io/docs/pricing) for what these mean. ## Add credits `POST /v1/credits/checkout` Creates a Stripe Checkout session for a credit purchase and returns its URL. Send the user there to pay. The credits are added to your balance automatically once the payment completes. | Field | Type | Description | |---|---|---| | `amount_usd` | number | Amount to charge, from 5 to 10,000. | ```bash curl https://api.getmux.io/v1/credits/checkout \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"amount_usd": 50}' ``` ```ts const { url, credits_usd, fee_usd } = await mux.checkout(50); console.log(url); ``` ```python session = mux.checkout(50) print(session["url"], session["credits_usd"], session["fee_usd"]) ``` ```json { "url": "https://checkout.stripe.com/c/pay/cs_live_...", "credits_usd": 47.25, "fee_usd": 2.75 } ``` `amount_usd` is what you pay. `credits_usd` is what lands in your balance after the purchase fee (`fee_usd`). An amount outside 5 to 10,000 returns `400 invalid_request`. ## Ledger `GET /v1/credits/ledger` Every change to your balance, newest first. | Query parameter | Default | Description | |---|---|---| | `kind` | `purchase,grant,byok_fee` | Comma-separated list of entry kinds to include. | | `limit` | `50` | Page size, 1 to 200. | | `offset` | `0` | Entries to skip. | Entry kinds: | Kind | Meaning | |---|---| | `purchase` | Credits added by a purchase (after the fee). | | `grant` | Credits added by Mux, for example a promotional grant. | | `byok_fee` | A fee charged for a [BYOK](https://getmux.io/docs/byok) request past the free tier. | | `usage` | The price of a search request. | | `refund` | A charge refunded because the provider call failed. | | `chargeback` | Credits taken back because a card payment was refunded or disputed (positive again if a dispute is resolved in your favour). | By default the ledger shows money in and BYOK fees. Ask for per-request entries with `kind=usage,refund`. ```bash curl "https://api.getmux.io/v1/credits/ledger?kind=purchase,grant&limit=20" \ -H "Authorization: Bearer $MUX_API_KEY" ``` ```json { "data": [ { "kind": "purchase", "amount_usd": 47.25, "created_at": "2026-10-12T18:04:09Z" }, { "kind": "grant", "amount_usd": 5.0, "created_at": "2026-10-10T09:30:00Z" } ] } ``` `amount_usd` is positive for credits added and negative for charges. ## Usage `GET /v1/usage` Spend and request counts over a recent window, aggregated three ways. Only successful requests are counted. | Query parameter | Default | Description | |---|---|---| | `days` | `30` | Window length in days, 1 to 90. | ```bash curl "https://api.getmux.io/v1/usage?days=7" \ -H "Authorization: Bearer $MUX_API_KEY" ``` ```json { "days": 7, "daily": [ { "date": "2026-10-11", "requests": 1240, "cost_usd": 3.41 }, { "date": "2026-10-12", "requests": 980, "cost_usd": 2.77 } ], "by_model": [ { "model": "exa/auto", "requests": 700, "cost_usd": 4.9 }, { "model": "parallel/fast", "requests": 1520, "cost_usd": 1.52 } ], "by_key": [ { "key_id": "key_Qx9...", "name": "agent-7", "requests": 1800, "cost_usd": 5.2 }, { "key_id": "key_Zp2...", "name": "default", "requests": 420, "cost_usd": 1.22 } ] } ``` - `daily` has one entry per UTC day that had traffic. Days with no requests are omitted. - `by_model` and `by_key` are sorted by cost, highest first. Deleted keys still appear in `by_key` with their past usage. - Usage is recorded just after a request returns, so the newest requests can take a moment to appear. ## Activity `GET /v1/activity` Your most recent requests, newest first. | Query parameter | Default | Description | |---|---|---| | `limit` | `50` | Page size, 1 to 200. | | `offset` | `0` | Entries to skip. | ```bash curl "https://api.getmux.io/v1/activity?limit=2" \ -H "Authorization: Bearer $MUX_API_KEY" ``` ```json { "data": [ { "id": "req_9fVx2kQ0b1c3dEfG", "model": "parallel/fast", "status": "ok", "cost_usd": 0.001, "latency_ms": 742, "results": 10, "error": null, "key_name": "agent-7", "created_at": "2026-10-12T18:21:07Z" }, { "id": "req_Hk3p...:exa/auto:platform", "model": "exa/auto", "status": "error", "cost_usd": 0.0, "latency_ms": 15003, "results": null, "error": "exa timeout", "key_name": "agent-7", "created_at": "2026-10-12T18:20:44Z" } ] } ``` A successful request has the request `id` and `status: "ok"`. Each upstream attempt that failed is logged as its own entry with `status: "error"` and the reason in `error`. Its cost is `0` because failed attempts are refunded, except for a provider timeout, which keeps the model's base price (see [Pricing](https://getmux.io/docs/pricing)). A request that fell back to another model therefore shows an error entry followed by the successful one. --- # Pricing What Mux costs. Provider list prices passed through, plus a fee when you buy credits. Source: https://getmux.io/docs/pricing Mux charges the providers' own list prices for each request, with no per-request markup. The only fee is a small one when you buy credits. ## Per-request prices | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | The table above is loaded live from `GET /v1/models`. Prices are current list prices and can change; the price you see for a model is the price you pay. Each model's price covers **10 results** per request. ### Extra results Asking for more than 10 results costs a little more on Exa and Parallel: an additional per-result charge for every result beyond the tenth (shown in the table as the price per 1,000 extra results, per request). Keenable's and DeepSeek's prices don't change with result count. DeepSeek bills only searches that return at least one result, so a `deepseek/search` request that comes back empty costs nothing on Mux either. Every other provider bills an empty answer, and so does Mux. Extra results are billed on the results the provider returns, not the number you asked for, and never on more than you asked for. If you request 20 results and the provider returns 12, you pay for 2 extra results, not 10. Your balance is reserved for the full request up front and settled to the returned count as soon as the provider answers. Filters that Mux applies itself after the provider has answered (`published_before` on Parallel models, more than one domain on Keenable, filters that don't fit in DeepSeek's 128-character query, and the domain check Mux always re-runs) can reduce the results you see but not the price, because the provider has already billed for them. Pass filters the provider supports natively to keep the two numbers the same; see [How providers are normalized](https://getmux.io/docs/models#how-providers-are-normalized). For example, with `exa/auto` at $7.00 per 1,000 requests and $1.00 per 1,000 extra results: | `max_results` | Cost | |---|---| | 10 | $0.0070 | | 25 | $0.0070 + 15 × $0.0010 = $0.0220 | | 100 | $0.0070 + 90 × $0.0010 = $0.0970 | You are billed for the number of results you request, after it is capped to the model's maximum (50 for Parallel, Keenable and DeepSeek, 100 for Exa). Results removed by your domain filters still count, since the provider still returned them. ## Credit purchases Mux is prepaid. You buy credits with a card through Stripe, and requests draw them down. - **Minimum purchase:** $5. Maximum: $10,000 per purchase. - **Fee:** 5.5% of the amount, with a **minimum of $0.80**. The fee is taken from what you pay, so you receive credits equal to the amount minus the fee. | You pay | Fee | Credits added | |---|---|---| | $10 | $0.80 (minimum) | $9.20 | | $50 | $2.75 | $47.25 | | $100 | $5.50 | $94.50 | Because of the minimum, the fee is $0.80 for any purchase up to about $14.55, and 5.5% above that. Larger, less frequent top-ups are the cheapest way to buy. The live fee terms are available from `GET /v1/credits/pricing` (see [Credits and usage](https://getmux.io/docs/credits)). Credits are in US dollars. ## When you aren't charged - **A failed provider call.** Mux reserves the price before calling the provider and refunds it if the call fails. If it then falls back to another model, only the model that succeeds is charged. See [Models](https://getmux.io/docs/models). - **A rejected request.** Requests that fail validation, fail authentication, or are blocked for insufficient credits or by an API key's credit limit cost nothing. - **A fully failed request.** If every model fails you get `502 all_providers_failed` and your balance is unchanged, except for timed-out attempts (below). The exception is a **provider timeout** (no answer after 15 seconds). The provider has usually completed and billed the search by then, so a timed-out attempt keeps the model's base price and refunds only the extra-results portion. The charge is shown on that attempt in the response's `attempts` array. BYOK calls are always refunded in full, because the provider bills you directly. Refunds appear in your [ledger](https://getmux.io/docs/credits) as `refund` entries next to the `usage` entry they reverse. ## Bring your own keys If you connect your own provider account, requests to that provider are billed by the provider directly and Mux charges only a small fee after a generous free tier: - **The first 1,000,000 requests each month are free.** The count is per account, per calendar month (UTC), across all providers. - **After that, 5% of the model's list price** per request, deducted from your credits. For example, an `exa/auto` request with 10 results has a list price of $0.007, so the fee is $0.00035. - The fee is refunded if the call fails. See [Bring your own keys](https://getmux.io/docs/byok). ## Key limits You can cap how much an individual API key can spend with a [credit limit](https://getmux.io/docs/keys). Limits only restrict spending; they don't change prices. --- # Managing keys Create, limit, disable and delete API keys programmatically with a management key. Source: https://getmux.io/docs/keys A **management key** (`mux-mgmt-...`) can create and administer API keys. Use it to issue one key per customer or agent, each with its own spend cap, and to turn keys off instantly. Management keys can't run searches. See [Authentication](https://getmux.io/docs/authentication) for the difference between key types. All endpoints on this page require a management key and return `403 wrong_key_type` for an API key, except `GET /v1/key`, which works with either. ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); ``` ```python from getmux import Mux admin = Mux(api_key="mux-mgmt-...") ``` ## The key object ```json { "id": "key_Hx2mQp9aLwT4kZ1v", "name": "customer-acme", "label": "mux-AbCdE…", "type": "api", "limit": 25.0, "limit_remaining": 18.4, "limit_reset": "monthly", "disabled": false, "expires_at": "2027-01-01T00:00:00Z", "usage": 6.6, "usage_period": 6.6, "created_at": "2026-10-12T17:00:00Z", "updated_at": "2026-10-12T17:00:00Z" } ``` | Field | Description | |---|---| | `id` | Key id (`key_...`). Use this in the other endpoints. It is not the secret. | | `name` | Display name, or `null`. | | `label` | Masked prefix of the secret. The full secret is only ever returned once, on creation. | | `type` | `api` or `management`. | | `limit` | Credit limit in USD, or `null` for unlimited. | | `limit_remaining` | What is left of the limit in the current window, or `null` if unlimited. | | `limit_reset` | `daily`, `weekly`, `monthly`, or `null` (the limit never resets). | | `disabled` | `true` if the key is switched off. | | `expires_at` | RFC 3339 timestamp (UTC), or `null`. | | `usage` | Lifetime spend of this key in USD. | | `usage_period` | Spend in the current limit window. | | `created_at`, `updated_at` | RFC 3339 timestamps (UTC). | ## Create a key `POST /v1/keys` | Field | Type | Description | |---|---|---| | `name` | string | 1 to 100 characters. Optional. | | `limit` | number | Credit limit in USD, 0 to 1,000,000. Omit for unlimited. A limit of `0` creates a key that can't spend. | | `limit_reset` | string | `daily`, `weekly` or `monthly`. Optional. | | `expires_at` | string | RFC 3339 timestamp in the future, for example `2027-01-01T00:00:00Z`. Optional. | The response is `201` with the secret and the key object. **This is the only time the secret is returned.** ```bash curl https://api.getmux.io/v1/keys \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "customer-acme", "limit": 25, "limit_reset": "monthly"}' ``` ```ts const { key, data } = await admin.keys.create({ name: "customer-acme", limit: 25, limitReset: "monthly", }); // key -> "mux-..." (store it now) // data -> the key object ``` ```python secret, key = admin.keys.create("customer-acme", limit=25, limit_reset="monthly") # secret -> "mux-..." (store it now) # key -> ApiKey object ``` ```json { "key": "mux-3f9aQ...", "data": { "id": "key_Hx2mQp9aLwT4kZ1v", "name": "customer-acme", "limit": 25.0, "limit_remaining": 25.0, "limit_reset": "monthly", "...": "..." } } ``` Unknown fields return `400 invalid_request`. ## List keys `GET /v1/keys?offset=0&limit=100` Returns your API keys (not management keys), newest first. `limit` is 1 to 100 (default 100); `offset` defaults to 0. ```bash curl "https://api.getmux.io/v1/keys?limit=20" \ -H "Authorization: Bearer $MUX_MGMT_KEY" ``` ```ts const keys = await admin.keys.list({ limit: 20 }); ``` ```python keys = admin.keys.list(limit=20) ``` ```json { "data": [ { "id": "key_Hx2mQp9aLwT4kZ1v", "name": "customer-acme", "...": "..." } ] } ``` ## Get a key `GET /v1/keys/{id}` ```bash curl https://api.getmux.io/v1/keys/key_Hx2mQp9aLwT4kZ1v \ -H "Authorization: Bearer $MUX_MGMT_KEY" ``` ```ts const key = await admin.keys.get("key_Hx2mQp9aLwT4kZ1v"); ``` ```python key = admin.keys.get("key_Hx2mQp9aLwT4kZ1v") ``` Returns `{ "data": { ...key object } }`, or `404 not_found`. You can only see keys in your own account. ## Update a key `PATCH /v1/keys/{id}` Send only the fields you want to change. Fields you leave out are unchanged; sending `null` clears a field. | Field | Type | Description | |---|---|---| | `name` | string or null | New name (1 to 100 characters), or `null` to clear it. | | `limit` | number or null | New limit in USD, or `null` to remove the limit. | | `limit_reset` | string or null | `daily`, `weekly`, `monthly`, or `null`. | | `disabled` | boolean | `true` switches the key off; `false` switches it back on. | Expiry can only be set when a key is created. ```bash # raise the limit curl -X PATCH https://api.getmux.io/v1/keys/key_Hx2mQp9aLwT4kZ1v \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{"limit": 50}' ``` ```ts await admin.keys.update(id, { limit: 50 }); // raise the limit await admin.keys.update(id, { limit: null }); // remove the limit await admin.keys.update(id, { disabled: true }); // kill switch ``` ```python admin.keys.update(id, limit=50) # raise the limit admin.keys.update(id, limit=None) # remove the limit admin.keys.update(id, disabled=True) # kill switch ``` Lowering a limit below what the key has already spent in the current window doesn't refund anything; the key is simply blocked until the window resets. ## Delete a key `DELETE /v1/keys/{id}` Permanently stops the key from working. Returns `{ "deleted": true }`, or `404 not_found` if it doesn't exist. Its past usage still counts toward your totals. ```bash curl -X DELETE https://api.getmux.io/v1/keys/key_Hx2mQp9aLwT4kZ1v \ -H "Authorization: Bearer $MUX_MGMT_KEY" ``` ```ts await admin.keys.delete("key_Hx2mQp9aLwT4kZ1v"); ``` ```python admin.keys.delete("key_Hx2mQp9aLwT4kZ1v") ``` ## Inspect the current key `GET /v1/key` Returns the key object for the key that made the call. Works with API keys and management keys. This is how an agent can check its own remaining budget before starting expensive work. ```bash curl https://api.getmux.io/v1/key \ -H "Authorization: Bearer $MUX_API_KEY" ``` ```ts const me = await mux.key(); console.log(me.limitRemaining); ``` ```python me = mux.key() print(me.limit_remaining) ``` ## How limits work - **Limits are in USD** and count what the key spends on searches. They apply to the credit price of requests made with that key. - **Reset windows are in UTC:** `daily` resets at 00:00 UTC every day, `weekly` at 00:00 UTC on Monday, and `monthly` at 00:00 UTC on the 1st. With no `limit_reset`, the limit is a lifetime cap. - **They are enforced exactly.** The limit check and the balance check happen together, so a burst of concurrent requests can't push a key past its limit. - **A blocked request is free.** If a request would exceed the limit, it is rejected with `402 key_limit_exceeded` and nothing is charged. When you use `models` fallbacks, a model that doesn't fit under the limit is skipped and a cheaper one later in the list can still run. - **Failed calls give the budget back.** If a provider call fails and is refunded, the amount is also returned to the key's limit. - **BYOK fees** count against the key's limit like any other charge and are refunded if the call fails. See [Bring your own keys](https://getmux.io/docs/byok). | Situation | Response | |---|---| | Request would exceed the key's limit | `402 key_limit_exceeded` | | Key is disabled | `401 key_disabled` | | Key is past `expires_at` | `401 key_expired` | > **Note:** A change to a key can take up to about 15 seconds to take effect on every server. Disabling or deleting a key takes effect immediately for requests handled by the server that processed the change. ## Example: one key per customer with a monthly cap Give each of your customers their own key, capped at $25 a month and expiring in a year. If a customer's agent goes into a loop, it stops at $25 and doesn't touch your other customers. ```bash curl https://api.getmux.io/v1/keys \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "customer-acme", "limit": 25, "limit_reset": "monthly", "expires_at": "2027-10-12T00:00:00Z" }' ``` ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); async function provisionCustomer(name: string) { const { key, data } = await admin.keys.create({ name: `customer-${name}`, limit: 25, limitReset: "monthly", expiresAt: "2027-10-12T00:00:00Z", }); // Save `key` in your secret store now; it can't be retrieved again. return { secret: key, keyId: data.id }; } // Later: see how much a customer has used, or cut them off. const k = await admin.keys.get(keyId); console.log(`${k.usagePeriod} of ${k.limit} used this month`); await admin.keys.update(keyId, { disabled: true }); ``` ```python from getmux import Mux admin = Mux(api_key=os.environ["MUX_MGMT_KEY"]) def provision_customer(name: str): secret, key = admin.keys.create( f"customer-{name}", limit=25, limit_reset="monthly", expires_at="2027-10-12T00:00:00Z", ) # Save `secret` in your secret store now; it can't be retrieved again. return secret, key.id # Later: see how much a customer has used, or cut them off. k = admin.keys.get(key_id) print(f"{k.usage_period} of {k.limit} used this month") admin.keys.update(key_id, disabled=True) ``` --- # Bring your own keys Use your own Exa, Parallel, Keenable or DeepSeek account through Mux. Source: https://getmux.io/docs/byok If you already have an account with Exa, Parallel, Keenable or DeepSeek (negotiated rates, existing credits, or a contract), you can connect your own provider key. Requests to that provider are then made with **your** key and billed by the provider directly. You keep Mux's unified API, fallbacks, per-key limits and usage tracking. ## How it works 1. You store a provider key for one or more of `exa`, `parallel`, `keenable`, `deepseek`. 2. When a request is served by a model from that provider, Mux calls the provider with your key. 3. If your key fails (revoked, rate limited, out of credit at the provider), Mux retries the same model on Mux's own provider account and charges the normal list price, unless you set `always_use`. See below. Provider keys are managed with a [management key](https://getmux.io/docs/authentication). They can't be set with a regular API key. ## Add a provider key `PUT /v1/provider-keys/{provider}` | Field | Type | Default | Description | |---|---|---|---| | `api_key` | string | required | Your key from the provider. 8 to 512 characters, no whitespace. | | `always_use` | boolean | `false` | If `true`, never fall back to Mux's shared key when yours fails. | `{provider}` is `exa`, `parallel`, `keenable` or `deepseek`. Anything else returns `400 unknown_provider`. Calling it again for the same provider replaces the stored key. ```bash curl -X PUT https://api.getmux.io/v1/provider-keys/exa \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{"api_key": "your-exa-key", "always_use": false}' ``` ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); await admin.providerKeys.set("exa", "your-exa-key"); await admin.providerKeys.set("parallel", "your-parallel-key", { alwaysUse: true }); ``` ```python from getmux import Mux admin = Mux(api_key="mux-mgmt-...") admin.provider_keys.set("exa", "your-exa-key") admin.provider_keys.set("parallel", "your-parallel-key", always_use=True) ``` ```json { "data": { "provider": "exa", "hint": "…-key", "always_use": false } } ``` ## List and remove provider keys ```bash curl https://api.getmux.io/v1/provider-keys \ -H "Authorization: Bearer $MUX_MGMT_KEY" ``` ```ts const keys = await admin.providerKeys.list(); await admin.providerKeys.delete("exa"); ``` ```python keys = admin.provider_keys.list() admin.provider_keys.delete("exa") ``` ```json { "data": [ { "provider": "exa", "hint": "…-key", "always_use": false, "created_at": "2026-10-12T17:00:00Z" } ] } ``` `DELETE /v1/provider-keys/{provider}` returns `{ "deleted": true }`, or `404 not_found` if no key is stored for that provider. After deleting, requests to that provider are served and billed through Mux again. ## Your keys are protected - Provider keys are **encrypted at rest** and are never returned by the API. Listings show only a hint, the last four characters, for example `…1234`. - To change a key, set a new one. To remove it, delete it. > **Note:** Mux doesn't test a provider key when you save it. A mistyped or revoked key is discovered the first time it is used, and then Mux falls back to its own account (or fails, if `always_use` is on). Check a new key by running a search and looking at `attempts`. ## Fallback and `always_use` With a provider key stored and `always_use: false` (the default), each request to that provider has up to two attempts: 1. **Your key.** `source: "byok"`. 2. **Mux's key**, only if the first attempt failed. `source: "platform"`, billed at the normal list price. With `always_use: true` there is only the first attempt. If your key fails, that model fails, and if you passed a `models` list Mux moves on to the next model. Use `always_use` when you must never be billed through Mux for that provider, for example because of a data-handling agreement with the provider. The response shows what happened: ```json "attempts": [ { "model": "exa/auto", "source": "byok", "status": "error", "error": "exa 401: ..." }, { "model": "exa/auto", "source": "platform", "status": "ok" } ], "usage": { "cost_usd": 0.007, "results": 10, "latency_ms": 1210, "byok": false } ``` `usage.byok` is `true` only when your own key served the request. ## Billing - **The first 1,000,000 BYOK requests each month are free.** The count is per account, per calendar month (UTC), across all providers. - **After that, 5% of the model's list price** per request, deducted from your credits. An `exa/auto` request for 10 results has a list price of $0.007, so the fee is $0.00035. The provider bills you separately for the search itself. - **A failed BYOK call is refunded**, and does not use up one of your free requests. - A fee is shown in your [ledger](https://getmux.io/docs/credits) as `byok_fee`. - BYOK fees count against the API key's [credit limit](https://getmux.io/docs/keys) and appear in its usage, like any other charge. When the fee applies but your balance can't cover it, the BYOK attempt is skipped. If the same request can't be served any other way either, you get `402 insufficient_credits`. Within the free tier no credits are needed at all, so a BYOK request works even with a zero balance. ```json { "error": { "code": "insufficient_credits", "message": "Insufficient credits. Add credits at /v1/credits/checkout.", "needed_usd": 0.00035 } } ``` ## Not yet available - Validating a provider key against the provider when it is saved. ## Errors | Status | Code | Meaning | |---|---|---| | 400 | `unknown_provider` | The provider isn't `exa`, `parallel`, `keenable` or `deepseek`. | | 400 | `invalid_request` | `api_key` is missing, too short or long, or contains whitespace. | | 403 | `wrong_key_type` | You used an API key instead of a management key. | | 404 | `not_found` | No key is stored for that provider (on delete). | | 503 | `byok_disabled` | Bring-your-own-key isn't enabled on this deployment. | --- # Errors The error format, every error code Mux returns, and which errors to retry. Source: https://getmux.io/docs/errors Errors are JSON with a single `error` object. `code` is a stable, machine-readable string; `message` is for humans and may change. Some errors carry extra fields. ```json { "error": { "code": "insufficient_credits", "message": "Insufficient credits. Add credits at /v1/credits/checkout.", "needed_usd": 0.007 } } ``` Branch on the HTTP status and `code`, not on `message`. In the SDKs every non-2xx response throws a `MuxError` with `status`, `code` and `message` (and the parsed body). See [SDKs](https://getmux.io/docs/sdks). ## Error codes | Status | Code | Meaning | What to do | |---|---|---|---| | 400 | `invalid_request` | The body isn't valid JSON, has an unknown field, or a value is missing or out of range (for example `max_results` over 100, a bad date, or an amount outside 5 to 10,000). The `message` says which. | Fix the request. Don't retry unchanged. | | 400 | `unknown_model` | A model id in `model` or `models` doesn't exist. | Use an id from `GET /v1/models`. | | 400 | `unknown_provider` | The provider in a provider-keys path isn't `exa`, `parallel`, `keenable` or `deepseek`. | Fix the provider name. | | 401 | `invalid_api_key` | The `Authorization` header is missing, isn't `Bearer mux-...`, or the key doesn't exist or was deleted. | Check the key. | | 401 | `key_disabled` | The key has been switched off. | Re-enable it or use another key. | | 401 | `key_expired` | The key is past its `expires_at`. | Create a new key. | | 402 | `insufficient_credits` | Your balance can't cover any model that was attempted. Includes `needed_usd`, the price of the cheapest blocked attempt. | [Add credits](https://getmux.io/docs/credits). | | 402 | `key_limit_exceeded` | The request would take this API key past its credit limit. Includes `needed_usd`. Nothing was charged. | Raise or reset the [limit](https://getmux.io/docs/keys), or use another key. | | 403 | `wrong_key_type` | A management key was used to search, or an API key was used on a management endpoint. | Use the right [kind of key](https://getmux.io/docs/authentication). | | 403 | `account_suspended` | Mux has suspended the account; every key on it is refused. The dashboard shows the reason. | Email support@getmux.io. | | 404 | `not_found` | The key id or provider key doesn't exist in your account. | Check the id. | | 429 | `rate_limited` | This API key is sending requests faster than its rate limit. Nothing was charged. The `Retry-After` header says how many seconds to wait. | Slow down, or retry after the delay. The SDKs do this for you. | | 429 | `too_many_concurrent_requests` | The account already has too many searches in flight. Nothing was charged. | Wait for some requests to finish, or lower your concurrency. | | 502 | `all_providers_failed` | Every attempted model failed or was unavailable. Includes `attempts`. You weren't charged. | Retry, or add more models to `models`. | | 502 | `stripe_error` | Creating the Stripe Checkout session failed. | Retry shortly. | | 503 | `overloaded` | Every model you allowed is at capacity right now. Nothing was charged. Includes a `Retry-After` header. | Retry shortly, or add more models to `models` so Mux has somewhere to fall back to. | | 503 | `byok_disabled` | Provider keys can't be stored on this deployment. | Contact support. | | 503 | `stripe_disabled` | Credit purchases aren't available. | Contact support. | | 500 | `internal` | Something went wrong on our side. | Retry with backoff. | ### `insufficient_credits` and `key_limit_exceeded` Both return `402`. When you use `models` fallbacks, Mux skips any model it can't afford and tries the next one, so you only see a 402 when **every** model was blocked. If both kinds of block happened, you get `key_limit_exceeded`. ### `all_providers_failed` The `attempts` array shows what happened to each model, which makes this easy to debug: ```json { "error": { "code": "all_providers_failed", "message": "All providers failed.", "attempts": [ { "model": "exa/auto", "source": "platform", "status": "error", "error": "exa timeout" }, { "model": "keenable/pro", "status": "skipped", "error": "provider not configured" } ] } } ``` Each failed attempt was refunded, so your balance and the key's limit are unchanged, with one exception: an attempt whose error is a provider `timeout` (no answer after 15 seconds) keeps the model's base price, because the provider has normally completed the search by then. Such an attempt carries a `cost_usd` field. See [Pricing](https://getmux.io/docs/pricing). ### Rate limits Mux limits how fast one key can search and how many searches one account can run at once, so a runaway loop can't use up shared capacity. | Limit | Default | Error | |---|---|---| | Requests per second per API key | 20 sustained, bursts up to 40 | `429 rate_limited` | | Searches in flight per account | 50 | `429 too_many_concurrent_requests` | | Calls to one provider at a time | Shared across all customers | falls back to the next model, then `503 overloaded` | Successful search responses include `x-ratelimit-limit` (requests per second allowed) and `x-ratelimit-remaining` (requests left in the current burst). Rejected requests include `Retry-After`. Limited requests are never charged. If you need higher limits, [contact us](https://getmux.io/contact). ### Responses without the error format Requests that never reach the API handlers can get a plain response instead of the JSON error object: an unknown path returns `404`, a wrong HTTP method returns `405`, and a malformed query string (for example `?limit=abc`) returns `400`. Check the status first and parse the body only if it is JSON. ## Retrying - **Don't retry 4xx errors** (including `402`). They won't succeed until you change the request, the key or your balance. - **`502 all_providers_failed`** is safe to retry yourself: you weren't charged. The SDKs don't retry it automatically because the gateway has already tried every model you allowed. - **`500` and other `5xx`** (except `502`) and **`429`** are retried automatically by the SDKs, up to 2 retries (3 attempts in total) with exponential backoff starting at 300 ms. If the response has a `Retry-After` header, the SDKs wait at least that long (up to 10 seconds). You can change this with `maxRetries` (TypeScript) or `max_retries` (Python). - Retrying a search is safe: failed attempts are refunded, and a request that errors before succeeding isn't charged. Include the `x-mux-request-id` response header (or the `id` in the body) when contacting support about a specific request. --- # SDKs Reference for the official TypeScript and Python clients. Source: https://getmux.io/docs/sdks The SDKs are thin, typed clients over the [HTTP API](https://getmux.io/docs/search). They add retries, timeouts and typed errors, and read your key from the environment. ## Install TypeScript / JavaScript: ```bash npm install getmux ``` Python: ```bash uv add getmux ``` In a project without a `pyproject.toml`, run `uv init` first, or use `uv pip install getmux` to install into an existing environment. The TypeScript package works in Node 18+ and any runtime with a global `fetch`. The Python package requires Python 3.9+ and uses `httpx`. ## Create a client By default the client reads your key from the `MUX_API_KEY` environment variable. You can also pass it directly, which takes precedence over the environment. This is useful when keys come from a secrets manager or when one process needs several keys (for example a search key and a [management key](https://getmux.io/docs/keys)): ```ts const mux = new Mux({ apiKey: "mux-..." }); ``` ```python mux = Mux(api_key="mux-...") ``` All options: ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const custom = new Mux({ apiKey: "mux-...", baseUrl: "https://api.getmux.io/v1", timeoutMs: 30_000, maxRetries: 2, }); ``` ```python from getmux import Mux, AsyncMux mux = Mux() # reads MUX_API_KEY custom = Mux( api_key="mux-...", base_url="https://api.getmux.io/v1", timeout=30.0, max_retries=2, ) ``` | Option (TypeScript / Python) | Default | Description | |---|---|---| | `apiKey` / `api_key` | `MUX_API_KEY` env var | Your key. The constructor throws if neither is set. | | `baseUrl` / `base_url` | `MUX_BASE_URL` env var, else `https://api.getmux.io/v1` | API base URL. | | `timeoutMs` / `timeout` | 30,000 ms / 30.0 s | Per-request timeout. | | `maxRetries` / `max_retries` | `2` | Retries for `429` and `5xx` (except `502`). | | `fetch` (TypeScript only) | global `fetch` | A custom `fetch` implementation. | Use an API key (`mux-...`) for searching and a management key (`mux-mgmt-...`) for the `keys` and provider-key methods. See [Authentication](https://getmux.io/docs/authentication). ## Search ```ts const res = await mux.search({ query: "latest on fusion energy", model: "exa/auto", // or models: ["parallel/fast", "exa/auto"] maxResults: 10, includeDomains: ["nature.com"], excludeDomains: ["example.com"], publishedAfter: "2026-01-01", publishedBefore: "2026-12-31", content: "snippet", // or "full" providerOptions: {}, }); ``` ```python res = mux.search( "latest on fusion energy", model="exa/auto", # or models=["parallel/fast", "exa/auto"] max_results=10, include_domains=["nature.com"], exclude_domains=["example.com"], published_after="2026-01-01", published_before="2026-12-31", content="snippet", # or "full" provider_options={}, ) ``` Only `query` is required. The parameters are described in the [Search API](https://getmux.io/docs/search) reference. ### The response TypeScript returns a `SearchResponse`: | Field | Type | |---|---| | `id`, `model`, `provider`, `query` | `string` | | `results` | `{ title, url, snippet, content?, publishedDate }[]` | | `usage` | `{ costUsd, results, latencyMs, byok }` | | `attempts` | `{ model, source?, status, error? }[]` (`source` is `"byok"` or `"platform"`) | Python returns a `SearchResponse` dataclass: | Field | Type | |---|---| | `id`, `model`, `provider`, `query` | `str` | | `results` | `list[SearchResult]` with `title`, `url`, `snippet`, `content`, `published_date` | | `cost_usd`, `latency_ms` | `float`, `int` | | `byok` | `bool`, true if the request ran on your own provider key | | `attempts` | `list[dict]`, each `{model, source, status, error?}` | `cost_usd` is what this request cost you: the list price on Mux's keys, or the small BYOK fee (often $0) on your own. See [Bring your own keys](https://getmux.io/docs/byok). ## Other methods | Purpose | TypeScript | Python | |---|---|---| | List models and prices | `await mux.models()` | `mux.models()` | | Credit balance (USD) | `await mux.credits()` | `mux.credits()` | | Stripe Checkout link | `await mux.checkout(50)` | `mux.checkout(50)` | | Info on the calling key | `await mux.key()` | `mux.key()` | | Spend by day, model and key | `await mux.usage(30)` | `mux.usage(30)` | | Recent requests | `await mux.activity({ limit: 50 })` | `mux.activity(limit=50)` | | Credit history | `await mux.ledger()` | `mux.ledger()` | With an API key, `usage()` and `activity()` cover that key's own requests. `ledger()` and `checkout()` need a management key (`mux-mgmt-...`), as does everything under `keys` and `providerKeys`. ```ts const models = await mux.models(); // array of model objects const balance = await mux.credits(); // number, USD const { url, credits_usd, fee_usd } = await mux.checkout(50); const me = await mux.key(); // ApiKey: limit, limitRemaining, usage, ... const usage = await mux.usage(30); // { daily, byModel, byKey } with costUsd per row const recent = await mux.activity({ limit: 20 }); const history = await mux.ledger(); // purchases, grants and BYOK fees ``` ```python models = mux.models() # list of dicts balance = mux.credits() # float, USD session = mux.checkout(50) # {"url": ..., "credits_usd": ..., "fee_usd": ...} me = mux.key() # ApiKey: limit, limit_remaining, usage, ... usage = mux.usage(30) # {"daily": [...], "by_model": [...], "by_key": [...]} recent = mux.activity(limit=20) history = mux.ledger() # purchases, grants and BYOK fees ``` `usage`, `activity` and `ledger` work with an API key or a management key. The raw endpoints are described in [Credits and usage](https://getmux.io/docs/credits). Pass `kind: "usage"` (TS) or `kind="usage"` (Python) to `ledger` to list per-request charges instead. ## Key management Requires a management key. Details and the key object are on [Managing keys](https://getmux.io/docs/keys). ```ts const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); const { key, data } = await admin.keys.create({ name: "agent-7", limit: 25, limitReset: "monthly", // "daily" | "weekly" | "monthly" expiresAt: "2027-01-01T00:00:00Z", }); const keys = await admin.keys.list({ offset: 0, limit: 100 }); const one = await admin.keys.get(data.id); await admin.keys.update(data.id, { limit: 50 }); await admin.keys.update(data.id, { limit: null }); // remove the limit await admin.keys.update(data.id, { disabled: true }); await admin.keys.delete(data.id); ``` ```python admin = Mux(api_key=os.environ["MUX_MGMT_KEY"]) secret, key = admin.keys.create( "agent-7", limit=25, limit_reset="monthly", # "daily" | "weekly" | "monthly" expires_at="2027-01-01T00:00:00Z", ) keys = admin.keys.list(offset=0, limit=100) one = admin.keys.get(key.id) admin.keys.update(key.id, limit=50) admin.keys.update(key.id, limit=None) # remove the limit admin.keys.update(key.id, disabled=True) admin.keys.delete(key.id) ``` `keys.create` returns the secret exactly once: `{ key, data }` in TypeScript, `(secret, ApiKey)` in Python. ### Updating: omitted vs. `null` `keys.update` only changes what you pass. In TypeScript, a property that is missing from the object is left alone and `null` clears it. In Python, an argument you don't pass is left alone, and passing `None` clears it (`limit=None` removes the limit, `name=None` removes the name, `limit_reset=None` makes the limit non-resetting). ```ts await admin.keys.update(id, { name: "renamed" }); // only the name changes await admin.keys.update(id, { limit: null, limitReset: null }); // clear both ``` ```python admin.keys.update(id, name="renamed") # only the name changes admin.keys.update(id, limit=None, limit_reset=None) # clear both ``` ## Provider keys (BYOK) Requires a management key. See [Bring your own keys](https://getmux.io/docs/byok). ```ts await admin.providerKeys.set("exa", "your-exa-key"); await admin.providerKeys.set("parallel", "your-key", { alwaysUse: true }); const list = await admin.providerKeys.list(); // { provider, hint, alwaysUse, createdAt }[] await admin.providerKeys.delete("exa"); ``` ```python admin.provider_keys.set("exa", "your-exa-key") admin.provider_keys.set("parallel", "your-key", always_use=True) keys = admin.provider_keys.list() # list[ProviderKey] admin.provider_keys.delete("exa") ``` ## Errors Any non-2xx response raises `MuxError`, with the HTTP `status`, the error `code` and the `message`. See [Errors](https://getmux.io/docs/errors) for the list of codes. ```ts import { Mux, MuxError } from "getmux"; try { await mux.search({ query: "hello" }); } catch (e) { if (e instanceof MuxError && e.code === "insufficient_credits") { console.log("Add credits:", (await mux.checkout(20)).url); } else { throw e; } } ``` ```python from getmux import Mux, MuxError try: mux.search("hello") except MuxError as e: if e.code == "insufficient_credits": print("Add credits:", mux.checkout(20)["url"]) else: raise ``` `MuxError` has `status`, `code` and `message`, plus the raw error body (`body`) for extra fields like `attempts` or `needed_usd`. ## Retries and timeouts Both SDKs retry `429` and `5xx` responses except `502`, up to `maxRetries` / `max_retries` times (default 2), waiting 300 ms, then 600 ms. `4xx` errors, including `402`, are never retried. A request that exceeds the timeout fails with the underlying network error. ## Async (Python) `AsyncMux` has the same methods as `Mux`, as coroutines. Close it when you're done with `aclose()`. ```python import asyncio from getmux import AsyncMux async def main(): mux = AsyncMux() try: res = await mux.search("latest on fusion energy", model="parallel/fast") print(res.results[0].url) keys = await mux.keys.list() # with a management key await mux.provider_keys.list() finally: await mux.aclose() asyncio.run(main()) ``` The sync client has `close()`. --- # Pricing: pay provider list prices per search, no markup > Mux charges Exa, Parallel, Keenable and DeepSeek list prices per request, from $0.0008 a search. Prepaid credits with a 5.5% purchase fee, failed calls refunded, bring your own keys free up to 1M requests a month. Canonical: https://getmux.io/pricing ## How it works - Per request you pay the provider's current list price, from $0.0008 to $0.012, each including 10 results. There is no per-request markup. - Credits are prepaid: $5 to $10,000 per purchase, a 5.5% fee with a $0.80 minimum, no subscription, no expiry. - Bring your own provider key: no Mux fee for the first 1,000,000 such requests each calendar month, then 5% of list price per request. - Failed calls cost nothing. Mux reserves the price, refunds it if the provider fails, and with a `models` list tries the next model. A provider timeout after 15 seconds keeps the base price. - Extra results beyond 10 cost $0.001 each on Exa and Parallel, billed on what the provider returns. Keenable and DeepSeek prices are flat, and a DeepSeek search that returns nothing is free. - Spend limits per API key: a USD cap that resets daily, weekly or monthly, enforced atomically with the debit. ## Price per model | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Credit purchases | You pay | Fee | Credits added | |---|---|---| | $10.00 | $0.80 (minimum) | $9.20 | | $50.00 | $2.75 | $47.25 | | $100.00 | $5.50 | $94.50 | | $1000.00 | $55.00 | $945.00 | More: https://getmux.io/docs/pricing (billing rules), https://getmux.io/docs/byok (your own keys), https://getmux.io/refunds (refund policy). --- # Exa instant (exa/instant) > Exa instant, lowest-latency neural search. Available through Mux at Exa's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/exa/instant ## At a glance - Price: $0.0040 per request with 10 results included ($4.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 100 - Typical latency: ~300 ms - Provider: Exa (https://exa.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.004 | $4.00 | $400.00 | | 25 | $0.019 | $19.00 | $1900.00 | | 50 | $0.044 | $44.00 | $4400.00 | | 100 | $0.094 | $94.00 | $9400.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "exa/instant", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "exa/instant", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="exa/instant", max_results=10) ``` ## How Exa behaves through Mux - Full content: Full page text - Domain filters: Applied natively, and re-checked by Mux - published_before: Applied natively - provider_options: `category`, `userLocation`, `moderation`, `includeText`, `excludeText` Details: https://getmux.io/docs/models#how-providers-are-normalized. Exa's own documentation: https://exa.ai ## Pricing notes - No markup: Mux passes through Exa's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Exa key. - Bring your own Exa key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Exa fast (exa/fast) > Exa fast. Available through Mux at Exa's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/exa/fast ## At a glance - Price: $0.0070 per request with 10 results included ($7.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 100 - Typical latency: ~500 ms - Provider: Exa (https://exa.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.007 | $7.00 | $700.00 | | 25 | $0.022 | $22.00 | $2200.00 | | 50 | $0.047 | $47.00 | $4700.00 | | 100 | $0.097 | $97.00 | $9700.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "exa/fast", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "exa/fast", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="exa/fast", max_results=10) ``` ## How Exa behaves through Mux - Full content: Full page text - Domain filters: Applied natively, and re-checked by Mux - published_before: Applied natively - provider_options: `category`, `userLocation`, `moderation`, `includeText`, `excludeText` Details: https://getmux.io/docs/models#how-providers-are-normalized. Exa's own documentation: https://exa.ai ## Pricing notes - No markup: Mux passes through Exa's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Exa key. - Bring your own Exa key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Exa auto (exa/auto) > Exa auto (default, best general quality). Available through Mux at Exa's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/exa/auto ## At a glance - Price: $0.0070 per request with 10 results included ($7.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 100 - Typical latency: ~1 s - Provider: Exa (https://exa.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.007 | $7.00 | $700.00 | | 25 | $0.022 | $22.00 | $2200.00 | | 50 | $0.047 | $47.00 | $4700.00 | | 100 | $0.097 | $97.00 | $9700.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "exa/auto", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "exa/auto", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="exa/auto", max_results=10) ``` ## How Exa behaves through Mux - Full content: Full page text - Domain filters: Applied natively, and re-checked by Mux - published_before: Applied natively - provider_options: `category`, `userLocation`, `moderation`, `includeText`, `excludeText` Details: https://getmux.io/docs/models#how-providers-are-normalized. Exa's own documentation: https://exa.ai ## Pricing notes - No markup: Mux passes through Exa's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Exa key. - Bring your own Exa key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Exa deep (exa/deep) > Exa deep multi-query search. Available through Mux at Exa's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/exa/deep ## At a glance - Price: $0.0120 per request with 10 results included ($12.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 100 - Typical latency: ~5 s - Provider: Exa (https://exa.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.012 | $12.00 | $1200.00 | | 25 | $0.027 | $27.00 | $2700.00 | | 50 | $0.052 | $52.00 | $5200.00 | | 100 | $0.102 | $102.00 | $10200.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "exa/deep", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "exa/deep", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="exa/deep", max_results=10) ``` ## How Exa behaves through Mux - Full content: Full page text - Domain filters: Applied natively, and re-checked by Mux - published_before: Applied natively - provider_options: `category`, `userLocation`, `moderation`, `includeText`, `excludeText` Details: https://getmux.io/docs/models#how-providers-are-normalized. Exa's own documentation: https://exa.ai ## Pricing notes - No markup: Mux passes through Exa's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Exa key. - Bring your own Exa key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Parallel turbo (parallel/turbo) > Parallel turbo, ~200ms. Available through Mux at Parallel's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/parallel/turbo ## At a glance - Price: $0.0010 per request with 10 results included ($1.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 50 - Typical latency: ~200 ms - Provider: Parallel (https://parallel.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.001 | $1.00 | $100.00 | | 25 | $0.016 | $16.00 | $1600.00 | | 50 | $0.041 | $41.00 | $4100.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "parallel/turbo", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "parallel/turbo", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="parallel/turbo", max_results=10) ``` ## How Parallel behaves through Mux - Full content: Excerpts joined together - Domain filters: Applied natively, and re-checked by Mux - published_before: Not applied upstream (Mux filters afterwards) - provider_options: `max_chars_total`, `client_model`, `advanced_settings.excerpt_settings`, `advanced_settings.location` Details: https://getmux.io/docs/models#how-providers-are-normalized. Parallel's own documentation: https://parallel.ai ## Pricing notes - No markup: Mux passes through Parallel's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Parallel key. - Bring your own Parallel key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Parallel fast (parallel/fast) > Parallel fast, <1s. Available through Mux at Parallel's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/parallel/fast ## At a glance - Price: $0.0010 per request with 10 results included ($1.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 50 - Typical latency: ~800 ms - Provider: Parallel (https://parallel.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.001 | $1.00 | $100.00 | | 25 | $0.016 | $16.00 | $1600.00 | | 50 | $0.041 | $41.00 | $4100.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "parallel/fast", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "parallel/fast", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="parallel/fast", max_results=10) ``` ## How Parallel behaves through Mux - Full content: Excerpts joined together - Domain filters: Applied natively, and re-checked by Mux - published_before: Not applied upstream (Mux filters afterwards) - provider_options: `max_chars_total`, `client_model`, `advanced_settings.excerpt_settings`, `advanced_settings.location` Details: https://getmux.io/docs/models#how-providers-are-normalized. Parallel's own documentation: https://parallel.ai ## Pricing notes - No markup: Mux passes through Parallel's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Parallel key. - Bring your own Parallel key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Parallel basic (parallel/basic) > Parallel basic. Available through Mux at Parallel's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/parallel/basic ## At a glance - Price: $0.0050 per request with 10 results included ($5.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 50 - Typical latency: ~1 s - Provider: Parallel (https://parallel.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.005 | $5.00 | $500.00 | | 25 | $0.020 | $20.00 | $2000.00 | | 50 | $0.045 | $45.00 | $4500.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "parallel/basic", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "parallel/basic", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="parallel/basic", max_results=10) ``` ## How Parallel behaves through Mux - Full content: Excerpts joined together - Domain filters: Applied natively, and re-checked by Mux - published_before: Not applied upstream (Mux filters afterwards) - provider_options: `max_chars_total`, `client_model`, `advanced_settings.excerpt_settings`, `advanced_settings.location` Details: https://getmux.io/docs/models#how-providers-are-normalized. Parallel's own documentation: https://parallel.ai ## Pricing notes - No markup: Mux passes through Parallel's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Parallel key. - Bring your own Parallel key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Parallel advanced (parallel/advanced) > Parallel advanced, highest quality excerpts. Available through Mux at Parallel's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/parallel/advanced ## At a glance - Price: $0.0050 per request with 10 results included ($5.00 per 1,000 requests) - Extra results beyond 10: $0.001 each, billed on what the provider returns - Maximum results per request: 50 - Typical latency: ~3 s - Provider: Parallel (https://parallel.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.005 | $5.00 | $500.00 | | 25 | $0.020 | $20.00 | $2000.00 | | 50 | $0.045 | $45.00 | $4500.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "parallel/advanced", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "parallel/advanced", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="parallel/advanced", max_results=10) ``` ## How Parallel behaves through Mux - Full content: Excerpts joined together - Domain filters: Applied natively, and re-checked by Mux - published_before: Not applied upstream (Mux filters afterwards) - provider_options: `max_chars_total`, `client_model`, `advanced_settings.excerpt_settings`, `advanced_settings.location` Details: https://getmux.io/docs/models#how-providers-are-normalized. Parallel's own documentation: https://parallel.ai ## Pricing notes - No markup: Mux passes through Parallel's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Parallel key. - Bring your own Parallel key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Keenable pro (keenable/pro) > Keenable pro index. Available through Mux at Keenable's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/keenable/pro ## At a glance - Price: $0.0040 per request with 10 results included ($4.00 per 1,000 requests) - Extra results beyond 10: no charge, the price is flat - Maximum results per request: 50 - Typical latency: ~700 ms - Provider: Keenable (https://keenable.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.004 | $4.00 | $400.00 | | 25 | $0.004 | $4.00 | $400.00 | | 50 | $0.004 | $4.00 | $400.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "keenable/pro", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "keenable/pro", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="keenable/pro", max_results=10) ``` ## How Keenable behaves through Mux - Full content: Not available (content is null) - Domain filters: One include domain natively (as site); more are filtered by Mux - published_before: Applied natively - provider_options: none Details: https://getmux.io/docs/models#how-providers-are-normalized. Keenable's own documentation: https://keenable.ai ## Pricing notes - No markup: Mux passes through Keenable's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Keenable key. - Bring your own Keenable key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Keenable realtime (keenable/realtime) > Keenable realtime index. Available through Mux at Keenable's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/keenable/realtime ## At a glance - Price: $0.0040 per request with 10 results included ($4.00 per 1,000 requests) - Extra results beyond 10: no charge, the price is flat - Maximum results per request: 50 - Typical latency: ~700 ms - Provider: Keenable (https://keenable.ai) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.004 | $4.00 | $400.00 | | 25 | $0.004 | $4.00 | $400.00 | | 50 | $0.004 | $4.00 | $400.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "keenable/realtime", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "keenable/realtime", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="keenable/realtime", max_results=10) ``` ## How Keenable behaves through Mux - Full content: Not available (content is null) - Domain filters: One include domain natively (as site); more are filtered by Mux - published_before: Applied natively - provider_options: none Details: https://getmux.io/docs/models#how-providers-are-normalized. Keenable's own documentation: https://keenable.ai ## Pricing notes - No markup: Mux passes through Keenable's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own Keenable key. - Bring your own Keenable key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # DeepSeek search (deepseek/search) > DeepSeek search, with a summary per result. Available through Mux at DeepSeek's list price with the same request and response shape as every other model. Canonical: https://getmux.io/models/deepseek/search ## At a glance - Price: $0.0008 per request with 10 results included ($0.75 per 1,000 requests) - Extra results beyond 10: no charge, the price is flat - Maximum results per request: 50 - Typical latency: ~800 ms - Provider: DeepSeek (https://api-docs.deepseek.com/guides/search/) - Failed requests are refunded; with a `models` list Mux falls back to the next model and bills only the one that answered ## Cost per request by result count | max_results | Per request | 1,000 requests | 100,000 requests | |---|---|---|---| | 10 | $0.0008 | $0.75 | $75.00 | | 25 | $0.0008 | $0.75 | $75.00 | | 50 | $0.0008 | $0.75 | $75.00 | ## Call it ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "open source vector databases", "model": "deepseek/search", "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "open source vector databases", model: "deepseek/search", maxResults: 10 }); ``` ```python from getmux import Mux mux = Mux() # reads MUX_API_KEY res = mux.search("open source vector databases", model="deepseek/search", max_results=10) ``` ## How DeepSeek behaves through Mux - Full content: Full page text - Domain filters: Applied natively (as site: operators) when they fit in the 128-character query, and re-checked by Mux - published_before: Applied natively (as a query operator), and re-checked by Mux - provider_options: `ori_question`, `localization` Details: https://getmux.io/docs/models#how-providers-are-normalized. DeepSeek's own documentation: https://api-docs.deepseek.com/guides/search/ ## Pricing notes - No markup: Mux passes through DeepSeek's list price. Mux's fee is 5.5% (minimum $0.80) when you buy credits, or 5% of list price after 1,000,000 monthly requests on your own DeepSeek key. - Bring your own DeepSeek key: https://getmux.io/docs/byok - All models and prices: https://getmux.io/models or GET https://api.getmux.io/v1/models --- # Add a web search tool to the Vercel AI SDK with Mux Build a web search tool for the Vercel AI SDK with the tool() helper and multi-step generateText, using Mux for Exa and Parallel results on one key. Source: https://getmux.io/guides/web-search-tool-vercel-ai-sdk A web search tool for the Vercel AI SDK is a `tool()` definition whose `execute` function calls a search API and returns a small list of results the model can read. This guide wires that tool to Mux, so one `MUX_API_KEY` gives the agent Exa, Parallel and Keenable with automatic fallback, and shows how to run it in a multi-step `generateText` or `streamText` loop. The code targets AI SDK 7 (`ai` 7.0.x) with Zod 4. ## What you need - Node 18 or newer. - A Mux API key from the [dashboard](https://getmux.io/dashboard/keys) with a few dollars of credit. See the [quickstart](https://getmux.io/docs/quickstart). - A key for whichever model provider you use with the AI SDK. The example below uses the Anthropic provider and reads `ANTHROPIC_API_KEY`. ```bash npm install ai @ai-sdk/anthropic zod getmux export MUX_API_KEY="mux-..." export ANTHROPIC_API_KEY="sk-ant-..." ``` The versions this guide was written against: | Package | Version | |---|---| | `ai` | 7.0.143 | | `@ai-sdk/anthropic` | 4.0.82 | | `zod` | 4.6.7 | | `getmux` | 0.1.0 | AI SDK 7 renamed a few things from AI SDK 6. Tools take `inputSchema` (not `parameters`), the step limit is `stopWhen: isStepCount(n)` (`stepCountIs` still works as a deprecated alias), and the per-step callback is `onStepEnd`. If you are on AI SDK 5 or 6, swap those names and the rest is identical. ## The tool Create `web-search.ts`. The schema exposes `query`, an optional `maxResults` and an optional `includeDomains`. The `execute` function calls Mux with an ordered fallback list and trims the response to four fields per result, which keeps tool output around a few hundred tokens instead of several thousand. ```ts // web-search.ts import { tool } from "ai"; import { z } from "zod"; import { Mux, MuxError } from "getmux"; // Reads MUX_API_KEY from the environment. const mux = new Mux(); export const webSearch = tool({ description: "Search the web for current information. Returns titles, URLs, short snippets and publish dates. Use it for anything after your training data or that needs a source.", inputSchema: z.object({ query: z.string().min(1).max(2000).describe("The search query, phrased like a web search"), maxResults: z.number().int().min(1).max(20).optional().describe("How many results to return (default 5)"), includeDomains: z .array(z.string()) .optional() .describe("Only return results from these domains, e.g. ['arxiv.org']"), }), execute: async ({ query, maxResults = 5, includeDomains }) => { try { const res = await mux.search({ query, models: ["parallel/fast", "exa/auto"], maxResults, includeDomains, }); return { model: res.model, results: res.results.map((r) => ({ title: r.title, url: r.url, snippet: r.snippet, published_date: r.publishedDate, })), }; } catch (e) { if (e instanceof MuxError) { // Return the problem as data so the model can tell the user instead of crashing the run. return { error: e.code, message: e.message, results: [] }; } throw e; } }, }); ``` Two choices worth explaining. `models: ["parallel/fast", "exa/auto"]` tries [`parallel/fast`](https://getmux.io/models/parallel/fast) first at $0.001 per search and falls back to [`exa/auto`](https://getmux.io/models/exa/auto) at $0.007 only if Parallel fails or is at capacity. You pay for the model that answered; failed attempts are refunded. Other orders are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). And the result object omits `content`, `usage` and `attempts`, because the model does not need them. Log `res.usage.costUsd` and `res.attempts` on your side if you want them. ## Multi-step generateText The agent loop is one call. `stopWhen: isStepCount(5)` lets the model search, read the results, search again if it wants, and then answer, up to five model calls. ```ts // agent.ts import { generateText, isStepCount } from "ai"; import { anthropic } from "@ai-sdk/anthropic"; import { webSearch } from "./web-search"; const result = await generateText({ model: anthropic("claude-opus-5-5"), tools: { webSearch }, stopWhen: isStepCount(5), system: "You are a research assistant. Use webSearch for current facts and cite the URL of every source you use.", prompt: "What changed in the EU AI Act implementation timeline this month?", onStepEnd: ({ toolCalls }) => { for (const call of toolCalls) console.log("searched:", call.input); }, }); console.log(result.text); console.log(`${result.steps.length} steps`); ``` Run it with `npx tsx agent.ts`. `result.steps` holds every step with its tool calls and tool results, which is useful for building a sources list from the URLs the model actually looked at. ## Streaming with streamText `streamText` takes the same options. The text stream only yields text, so the tool calls happen silently between chunks. Iterate `result.stream` instead if you want to show a "searching..." indicator when a `tool-call` part arrives. ```ts import { streamText, isStepCount } from "ai"; import { anthropic } from "@ai-sdk/anthropic"; import { webSearch } from "./web-search"; const result = streamText({ model: anthropic("claude-opus-5-5"), tools: { webSearch }, stopWhen: isStepCount(5), prompt: "Summarize this week's news about solid-state batteries with sources.", }); for await (const part of result.stream) { if (part.type === "tool-call") process.stdout.write("\n[searching] "); if (part.type === "text-delta") process.stdout.write(part.text); } ``` In a Next.js route handler, return `result.toUIMessageStreamResponse()` and the `useChat` hook renders tool calls as parts of the message. ## Tuning the tool - **Recency.** Add `publishedAfter` to the schema and pass it through. Mux normalizes dates to `YYYY-MM-DD` across providers. Note that `publishedBefore` is not applied by Parallel models. - **More text per result.** `content: "full"` returns page text on Exa and joined excerpts on Parallel. It is much larger, so use it in a separate `readPage`-style tool rather than in every search. - **Quality over price.** Swap the fallback order to `["exa/auto", "parallel/advanced"]` for research-heavy agents. The [models page](https://getmux.io/docs/models) lists latency and price for each option, and [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa) explains the trade-off. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Cost control An agent in a loop can issue many searches, and the AI SDK's step limit caps model calls, not spend. Put a hard ceiling on the Mux side by giving the agent its own key with a credit limit. Create it once with a management key (`mux-mgmt-...`): ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); const { key } = await admin.keys.create({ name: "research-agent", limit: 10, // USD limitReset: "daily", // or "weekly" | "monthly" }); // Put `key` in MUX_API_KEY for the agent process. It is shown once. ``` When the key reaches its limit, Mux returns `402 key_limit_exceeded` and charges nothing. The dashboard shows spend per key, so one key per agent or per customer gives you a usage breakdown for free. Details on [managing keys](https://getmux.io/docs/keys) and in the guide to [spend limits per API key](https://getmux.io/guides/per-agent-spend-limits). ## Handling 402 and 429 The `getmux` client already retries `429 rate_limited` and most `5xx` responses twice with backoff, honoring `Retry-After`. The errors you should handle yourself: | Error | Meaning | What to do in a tool | |---|---|---| | `402 insufficient_credits` | Account balance is empty | Return an error object so the model says it cannot search right now; alert an operator to [add credits](https://getmux.io/docs/credits). | | `402 key_limit_exceeded` | This key hit its daily, weekly or monthly limit | Same as above. Raise the limit or wait for the reset. | | `429 rate_limited` | More than 20 requests per second on one key | Already retried by the SDK. If it still surfaces, lower the agent's concurrency. | | `429 too_many_concurrent_requests` | More than 50 searches in flight on the account | Also retried. Queue tool executions if you run many agents in parallel. | | `502 all_providers_failed` | Every model in `models` failed | Not retried by the SDK. Safe to retry once; nothing was charged. | The `catch` block in the tool above turns every `MuxError` into data rather than throwing. That is usually the right call inside an agent, because a thrown error aborts the whole `generateText` run, while an error object lets the model explain the problem or answer from what it already knows. Switch on `e.code` if you want different behavior for billing errors versus transient ones. The full list is on the [errors page](https://getmux.io/docs/errors). ## FAQ ### Why not use a provider's SDK directly as the tool? You can, and at very high volume with a negotiated contract a direct relationship is cheaper. Mux is for the case where you want more than one index behind the same tool: `models` gives you fallback when a provider is slow or down, the response shape is the same for all three, and billing, rate limits and per-key budgets are in one place. ### Does the AI SDK's step limit stop runaway search costs? Only indirectly. `isStepCount(5)` caps the number of model calls, and the model may call the tool several times within one step. The per-key credit limit on the Mux side is the hard stop, and it is enforced atomically before the search runs. ### Can I use this tool with OpenAI or Google models? Yes. The tool is model-agnostic. Install the matching provider package, for example `@ai-sdk/openai`, and pass that provider's model to `generateText`. Nothing in `web-search.ts` changes. ### How do I keep the model from flooding its context with results? Keep `maxResults` small by default (5 is plenty for most questions), return only title, URL, snippet and date as the example does, and reserve `content: "full"` for a separate tool the model calls on one URL at a time. Get a key and run your first search in the [quickstart](https://getmux.io/docs/quickstart). --- # Exa pricing explained: what each Exa search tier costs Exa pricing through Mux: per-request and extra-result math for exa/instant, fast, auto and deep, worked examples, and when your own Exa contract is cheaper. Source: https://getmux.io/guides/exa-pricing-explained Exa pricing on Mux is per request, with 10 results included and a flat charge for each result past the tenth. The four tiers are `exa/instant` at $4.00 per 1,000 requests, `exa/fast` and `exa/auto` at $7.00, and `exa/deep` at $12.00, each plus $1.00 per 1,000 extra results. These are Exa's list prices passed through with no markup; Mux's only fee is 5.5% when you buy credits. This guide covers how the math works, what a month of searches costs at different volumes, what full-page content costs, which Exa options you can pass through, and when a direct Exa contract is the better deal. For Exa's own pricing and plans, see [exa.ai/pricing](https://exa.ai/pricing). For how these tiers stack up against Parallel's, see [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa). ## The four Exa tiers on Mux Exa is a neural search engine built for machine consumers. On Mux its search types appear as four model ids, each with its own price and latency profile. | Model | What it is | $/1k requests | $/1k extra results | Max results | Typical latency | |---|---|---|---|---|---| | `exa/instant` | Lowest-latency neural search | 4.00 | 1.00 | 100 | ~300 ms | | `exa/fast` | Fast search | 7.00 | 1.00 | 100 | ~500 ms | | `exa/auto` | Default, best general quality | 7.00 | 1.00 | 100 | ~1 s | | `exa/deep` | Multi-query deep search | 12.00 | 1.00 | 100 | ~5 s | Two things to notice. `exa/fast` and `exa/auto` cost the same, so the choice between them is latency versus quality, not price. And [`exa/deep`](https://getmux.io/models/exa/deep) runs several queries upstream for one request, which is why it costs more and takes longer. Mux's `auto` routing tries [`parallel/fast`](https://getmux.io/models/parallel/fast) first, so if you want Exa you need to name an Exa model explicitly, for example [`exa/auto`](https://getmux.io/models/exa/auto). The live table below is loaded from `GET /v1/models` and is the authority if prices change. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## The per-request math Every request includes 10 results. If you ask for more, each result beyond the tenth costs the extra rate, which is $0.001 on every Exa tier. The formula is: ``` cost = base_price + max(0, results_returned - 10) × 0.001 ``` Extra results are billed on what Exa actually returns, capped at what you asked for. Request 20, get 14, pay for 4 extra. Results that Mux later drops because of your domain filters still count, because Exa already returned them. Worked examples for three result counts: | Model | 10 results | 25 results | 100 results | |---|---|---|---| | `exa/instant` | $0.004 | $0.019 | $0.094 | | `exa/fast` | $0.007 | $0.022 | $0.097 | | `exa/auto` | $0.007 | $0.022 | $0.097 | | `exa/deep` | $0.012 | $0.027 | $0.102 | The extra-result charge dominates at high result counts. At 100 results the gap between `exa/instant` and `exa/deep` shrinks from 3x to about 8%, so if you routinely pull large result sets the tier matters less than the count. ## Monthly cost at volume Assuming 10 results per search, which is the default: | Model | 1,000 searches | 100,000 searches | 1,000,000 searches | |---|---|---|---| | `exa/instant` | $4 | $400 | $4,000 | | `exa/fast` | $7 | $700 | $7,000 | | `exa/auto` | $7 | $700 | $7,000 | | `exa/deep` | $12 | $1,200 | $12,000 | If your agents average 25 results per search, multiply the row by the 25-result figure instead: a million `exa/auto` searches at 25 results each is $22,000. Add the credit purchase fee on top. It is 5.5% of each purchase with a $0.80 minimum, so $100 buys $94.50 of credits and $1,000 buys $945. To fund $700 of `exa/auto` searches you pay about $741. Purchases are prepaid, $5 minimum and $10,000 maximum each, and credits do not expire. See [/docs/pricing](https://getmux.io/docs/pricing). ## What `content: "full"` costs Nothing extra on Mux. Setting `content: "full"` on an Exa model returns the full page text in each result's `content` field, and the price is the same as a snippet request with the same result count. If you are comparing against a direct Exa integration, check how Exa prices content retrieval on your plan at [exa.ai/pricing](https://exa.ai/pricing), since that is where the two bills can differ. ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "retrieval augmented generation evaluation", "model": "exa/auto", "max_results": 25, "content": "full"}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "retrieval augmented generation evaluation", model: "exa/auto", maxResults: 25, content: "full", }); console.log(res.usage.costUsd); // 0.022 ``` ```python from getmux import Mux mux = Mux() res = mux.search( "retrieval augmented generation evaluation", model="exa/auto", max_results=25, content="full", ) print(res.cost_usd) # 0.022 ``` The response's `usage.cost_usd` is the settled price for that request, so you never have to compute it yourself. ## Exa options you can pass through Mux normalizes the common fields (domains, dates, result count, content) and lets a few Exa-specific options through `provider_options`: - `category`, to restrict to a content type such as research papers or news - `userLocation`, to bias results by location - `moderation`, to filter unsafe content - `includeText` and `excludeText`, to require or forbid phrases in the page text Anything that would change the bill, such as the search type or result count, is rejected with `400 invalid_request`. None of the allowed options change the price. Values are passed to Exa as given; see the [Exa search reference](https://exa.ai/docs/reference/search) for accepted values and [/docs/models](https://getmux.io/docs/models) for the full allow-list. ## When BYOK with your own Exa contract is cheaper Mux bills Exa's list price. If you have a negotiated rate with Exa, prepaid Exa credits, or a plan that prices Exa searches below list, then paying Exa directly is cheaper per search. You can keep the Mux API and still do that by storing your Exa key with `PUT /v1/provider-keys/exa`. Requests then go out on your key and Exa bills you. The BYOK fee is zero for the first 1,000,000 BYOK requests per calendar month across all providers, then 5% of list price, which is $0.00035 per 10-result `exa/auto` search. Under a million Exa searches a month, BYOK costs you nothing on Mux at all. Above it, BYOK is cheaper than platform billing whenever your Exa contract rate is at least 5% below list. If your Exa key fails, Mux retries on its own account at list price unless you set `always_use: true`. Details are in [/docs/byok](https://getmux.io/docs/byok). The honest other side: if Exa is your only provider, your volume is high enough to negotiate, and you don't need fallbacks or per-key spend caps, a direct Exa contract with no intermediary is the simplest setup. Mux earns its place when you want the same key to [fall back to Parallel or Keenable](https://getmux.io/guides/search-fallbacks-for-ai-agents), or you need to [cap what each agent can spend](https://getmux.io/guides/per-agent-spend-limits). ## FAQ ### Does Mux mark up Exa's prices? No. Each Exa model is billed at Exa's list price per request with no per-request markup. Mux's only fee is 5.5% of each credit purchase, minimum $0.80, taken out of the amount you pay. With your own Exa key, the first million requests a month carry no Mux fee. ### Why do `exa/fast` and `exa/auto` cost the same? Both are $7.00 per 1,000 requests. `exa/fast` is tuned for latency, around 500 ms, while `exa/auto` picks Exa's best general approach at around 1 s. Choose by response time and result quality rather than price. ### Am I charged if an Exa request fails? No. A failed provider call is refunded and the next model in your `models` list is tried. The one exception is a 15-second timeout, which keeps the base price because Exa has usually completed the search by then. The charge shows on that attempt in the response's `attempts` array. ### Does asking for 100 results always cost $0.097 on `exa/auto`? Only if Exa returns 100. You are billed on the count returned, capped at what you requested, so a narrow query that returns 30 results costs $0.007 plus 20 extra results, or $0.027. Ready to try it? The [quickstart](https://getmux.io/docs/quickstart) gets you from signup to your first Exa search in a few minutes. --- # Keenable search API through Mux: models and pricing What the Keenable search API offers through Mux: two models, a flat $4 per 1,000 requests for up to 50 results, native date filters, and no page text. Source: https://getmux.io/guides/keenable-search-api The Keenable search API is available through Mux as two models, `keenable/pro` and `keenable/realtime`, at a flat $4 per 1,000 requests with no extra charge for results beyond ten, up to 50 per request. You call it with the same request body and get the same response shape as every other Mux model, so it drops into an existing integration or a fallback list without new parsing code. The trade-off is that Keenable returns snippets only; `content` is always `null`. ## What Keenable is [Keenable](https://keenable.ai) describes itself as independent web search infrastructure for AI, built on its own crawler, index and ranking. Its public pricing page lists an Agent Builder tier at $4 per 1,000 requests, as of October 2026, which is the list price Mux passes through. For anything beyond what this page covers, such as direct API access, a fetch endpoint or enterprise tiers, see [keenable.ai](https://keenable.ai). Through Mux, Keenable is one of four providers alongside Exa, Parallel and DeepSeek, and the first three are compared side by side in [Exa vs Parallel vs Keenable](https://getmux.io/compare/exa-vs-parallel-vs-keenable). Because it runs an independent index and returns snippets only, it is also the closest in shape to Brave among the providers Mux routes to; see [Brave Search API alternatives](https://getmux.io/compare/brave-search-api-alternatives). You do not need a Keenable account; your Mux key and credit balance cover it. If you already have a Keenable key, you can connect it with [BYOK](https://getmux.io/docs/byok) and Mux will route Keenable requests through it. ## The two models | Model | Description | $/1k requests | $/1k extra results | Max results | Typical latency | |---|---|---|---|---|---| | `keenable/pro` | Keenable pro index | 4.00 | 0 | 50 | ~700 ms | | `keenable/realtime` | Keenable realtime index | 4.00 | 0 | 50 | ~700 ms | Both cost the same and have the same limits. [`keenable/pro`](https://getmux.io/models/keenable/pro) is the general index and the one `auto` falls back to. [`keenable/realtime`](https://getmux.io/models/keenable/realtime) queries the realtime index, which is the one to pick when recency matters more than depth, for example news monitoring or "what happened today" questions. Latency is measured end to end through Mux and is typical, not guaranteed. The rest of the model catalog, with live prices, is below and on [/docs/models](https://getmux.io/docs/models). | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## How Keenable behaves behind the Mux request shape Mux translates each request into Keenable's native format and normalizes the answer. The differences from Exa and Parallel that affect how you write requests: | Feature | Keenable through Mux | |---|---| | `max_results` | 1 to 50. Asking for more is capped to 50. | | Extra results | No charge. 50 results cost the same $0.004 as 10. | | `include_domains` | One domain is sent natively as Keenable's `site` filter. With more than one, Mux filters the results after they come back. | | `exclude_domains` | Applied by Mux after the response. | | `published_after`, `published_before` | Both applied natively. | | `content: "full"` | Not available. `content` is `null` on every result; `snippet` is still filled. | | `provider_options` | None allow-listed yet. | Two consequences worth planning for. First, because the price is flat, request the full 50 results whenever you might use them; on Exa or Parallel the same request would add 40 extra-result charges. Second, because multi-domain and exclude filtering happen after the provider has answered, you can get fewer than `max_results` back and still pay the flat price. If you only need one domain, pass exactly one entry in `include_domains` so it is applied natively. ## When to pick Keenable - **You want many results at a flat price.** A 50-result `keenable/pro` request is $0.004. The same count on `exa/auto` is $0.007 plus 40 extra results at $0.001 each, or $0.047. For agents that cast a wide net and rank locally, the difference is more than 10x. - **You need fresh results.** `keenable/realtime` is a dedicated realtime index. Pair it with `published_after` to keep an agent's news or monitoring queries bounded to the last day or week. - **You want a third, independent provider in a fallback list.** Keenable runs its own index, so it is unlikely to fail at the same moment as Exa or Parallel. It is already the last entry in `auto`. - **You need `published_before`.** Keenable and Exa apply it natively; Parallel does not. ## When not to pick Keenable - **You need page text.** `content` is `null` on Keenable. If your agent reads full pages from search results, use [`exa/auto`](https://getmux.io/models/exa/auto) with `content: "full"` for page text, or a Parallel model for joined excerpts. Keenable also offers a separate fetch product directly; Mux does not expose it, so a direct Keenable relationship is the better choice if fetch is what you need. - **You need more than 50 results.** Exa models go to 100. - **You rely on provider-specific options.** Nothing is allow-listed for Keenable in `provider_options` yet. ## Calling Keenable through Mux The same endpoint and key as every other model. Set `model` to `keenable/pro` or `keenable/realtime`. ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "lithium battery recycling startups", "model": "keenable/pro", "max_results": 50, "published_after": "2026-01-01" }' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "lithium battery recycling startups", model: "keenable/pro", maxResults: 50, publishedAfter: "2026-01-01", }); for (const r of res.results) console.log(r.title, r.url, r.snippet); console.log(res.usage.costUsd); // 0.004 ``` ```python from getmux import Mux mux = Mux() res = mux.search( "lithium battery recycling startups", model="keenable/pro", max_results=50, published_after="2026-01-01", ) for r in res.results: print(r.title, r.url, r.snippet) print(res.cost_usd) # 0.004 ``` A realtime query for the last 24 hours, restricted to one site so the domain filter is applied natively: ```ts const res = await mux.search({ query: "federal reserve rate decision", model: "keenable/realtime", includeDomains: ["reuters.com"], publishedAfter: "2026-10-17", maxResults: 20, }); ``` ```python res = mux.search( "federal reserve rate decision", model="keenable/realtime", include_domains=["reuters.com"], published_after="2026-10-17", max_results=20, ) ``` ### In a fallback list Keenable works well as a cheap, independent backstop. This order tries Parallel first, Keenable second, and Exa last; failed attempts are refunded and `attempts` shows which model answered. The full rules are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). ```ts const res = await mux.search({ query: "kubernetes pod eviction thresholds", models: ["parallel/fast", "keenable/pro", "exa/auto"], }); console.log(res.model, res.attempts); ``` ```python res = mux.search( "kubernetes pod eviction thresholds", models=["parallel/fast", "keenable/pro", "exa/auto"], ) print(res.model, res.attempts) ``` If a request needs page text, put Keenable after the models that provide it, or leave it out, since a successful Keenable answer ends the fallback chain with `content` set to `null`. ## FAQ ### How much does a Keenable request cost through Mux? $0.004 per request at any result count from 1 to 50. That is Keenable's own list price with no per-request markup. Mux's only fee is 5.5% (minimum $0.80) when you buy credits. See [/pricing](https://getmux.io/pricing). ### What is the difference between keenable/pro and keenable/realtime? They query different indexes at the same price and limits. `pro` is the general index and the default in `auto`; `realtime` is the realtime index, intended for queries where freshness matters most. Both support native date filters. ### Can I get full page content from Keenable? Not through Mux. `content` is `null` on every Keenable result, even with `content: "full"`. Use an Exa model for full page text or a Parallel model for joined excerpts, or put one of them ahead of Keenable in your `models` list. ### Can I filter to several domains? Yes. Keenable natively supports one `site` domain, so a single `include_domains` entry is sent upstream. With several, Mux filters the results after they return, which can leave you with fewer than `max_results` at the same flat price. Run your first `keenable/pro` search in a minute with the [quickstart](https://getmux.io/docs/quickstart). --- # LangChain web search tool with Mux (Python and TypeScript) Build a LangChain web search tool with the @tool decorator or tool() helper and create_agent, backed by Mux for Exa, Parallel and Keenable on one key. Source: https://getmux.io/guides/web-search-tool-langchain A LangChain web search tool is a plain function wrapped with `@tool` in Python or `tool()` in TypeScript, with a schema that tells the model what arguments it takes. This guide builds one on top of Mux, so a single `MUX_API_KEY` gives the agent Exa, Parallel and Keenable with automatic fallback, then hands the tool to `create_agent` / `createAgent`. The code targets LangChain 1.x (Python `langchain` 1.4, TypeScript `langchain` 1.5). ## Setup You need a Mux API key from the [dashboard](https://getmux.io/dashboard/keys) with some credit, plus a key for your chat model. The examples use Anthropic's Claude; any LangChain chat model with tool calling works the same way. ```bash # Python uv add langchain langchain-core langchain-anthropic getmux # TypeScript npm install langchain @langchain/core @langchain/anthropic zod getmux export MUX_API_KEY="mux-..." export ANTHROPIC_API_KEY="sk-ant-..." ``` Versions used for this guide: | Package | Python | TypeScript | |---|---|---| | `langchain` | 1.4.6 | 1.5.18 | | `langchain-core` / `@langchain/core` | 1.6.12 | 1.2.18 | | `getmux` | 0.1.0 | 0.1.0 | | `pydantic` / `zod` | 2.14 | 4.6 | In LangChain 1.x the agent constructor is `create_agent` (Python) and `createAgent` (TypeScript). Both replace the older `create_react_agent` from LangGraph prebuilts and the `AgentExecutor` from LangChain 0.x. The `@tool` decorator lives in `langchain_core.tools` and is re-exported from `langchain.tools`; in TypeScript `tool()` lives in `@langchain/core/tools` and is re-exported from `langchain`. ## Define the tool The schema exposes `query`, an optional `max_results` and an optional `include_domains`. The body calls Mux with an ordered fallback list and returns only title, URL, snippet and publish date, so one search adds a few hundred tokens to the conversation instead of thousands. ```python # web_search.py from pydantic import BaseModel, Field from langchain_core.tools import tool from getmux import Mux, MuxError mux = Mux() # reads MUX_API_KEY class WebSearchInput(BaseModel): """Arguments for the web_search tool.""" query: str = Field(description="The search query, phrased like a web search") max_results: int = Field(default=5, ge=1, le=20, description="How many results to return") include_domains: list[str] | None = Field( default=None, description="Only return results from these domains, e.g. ['arxiv.org']" ) @tool(args_schema=WebSearchInput) def web_search(query: str, max_results: int = 5, include_domains: list[str] | None = None) -> dict: """Search the web for current information. Returns titles, URLs, short snippets and publish dates. Use it for anything recent or that needs a source.""" try: res = mux.search( query, models=["parallel/fast", "exa/auto"], max_results=max_results, include_domains=include_domains, ) except MuxError as e: # Hand the problem back as data so the agent can explain it instead of crashing. return {"error": e.code, "message": e.message, "results": []} return { "model": res.model, "results": [ { "title": r.title, "url": r.url, "snippet": r.snippet, "published_date": r.published_date, } for r in res.results ], } ``` ```ts // web-search.ts import { tool } from "@langchain/core/tools"; import { z } from "zod"; import { Mux, MuxError } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY export const webSearch = tool( async ({ query, maxResults, includeDomains }) => { try { const res = await mux.search({ query, models: ["parallel/fast", "exa/auto"], maxResults, includeDomains, }); return { model: res.model, results: res.results.map((r) => ({ title: r.title, url: r.url, snippet: r.snippet, published_date: r.publishedDate, })), }; } catch (e) { if (e instanceof MuxError) { return { error: e.code, message: e.message, results: [] }; } throw e; } }, { name: "web_search", description: "Search the web for current information. Returns titles, URLs, short snippets and publish dates. Use it for anything recent or that needs a source.", schema: z.object({ query: z.string().min(1).max(2000).describe("The search query, phrased like a web search"), maxResults: z.number().int().min(1).max(20).default(5).describe("How many results to return"), includeDomains: z .array(z.string()) .optional() .describe("Only return results from these domains, e.g. ['arxiv.org']"), }), }, ); ``` The docstring (Python) or `description` (TypeScript) is what the model reads when deciding whether to call the tool, so say what the tool is for and when to use it. LangChain serializes a dict or object return value to JSON for the tool message. `models: ["parallel/fast", "exa/auto"]` tries [`parallel/fast`](https://getmux.io/models/parallel/fast) first at $0.001 per search and falls back to [`exa/auto`](https://getmux.io/models/exa/auto) at $0.007 if Parallel errors or is at capacity. Failed attempts are refunded, so you pay for the one that answered. The `auto` default would add `keenable/pro` as a third fallback; list the models yourself when you want to control the order. Other orders for cost, quality and latency are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). ## Create the agent ```python # agent.py from langchain.agents import create_agent from web_search import web_search agent = create_agent( model="anthropic:claude-opus-5-5", tools=[web_search], system_prompt="You are a research assistant. Use web_search for current facts and cite the URL of every source you use.", ) result = agent.invoke( {"messages": [{"role": "user", "content": "What changed in the EU AI Act implementation timeline this month?"}]} ) print(result["messages"][-1].content) ``` ```ts // agent.ts import { createAgent } from "langchain"; import { webSearch } from "./web-search"; const agent = createAgent({ model: "anthropic:claude-opus-5-5", tools: [webSearch], systemPrompt: "You are a research assistant. Use web_search for current facts and cite the URL of every source you use.", }); const result = await agent.invoke({ messages: [{ role: "user", content: "What changed in the EU AI Act implementation timeline this month?" }], }); console.log(result.messages.at(-1)?.content); ``` `create_agent` runs the full loop: the model decides to call `web_search`, LangChain executes it, appends a tool message, and calls the model again until it answers without a tool call. `result["messages"]` holds the whole transcript, including each tool call's arguments and the JSON your tool returned, which is a convenient place to collect the URLs the agent actually consulted. Pass a model instance instead of the string if you need to set temperature or other options. ## Variations - **Recency filter.** Add `published_after: str | None` to the schema and pass it to `mux.search`. Dates are `YYYY-MM-DD` and normalized across providers. `published_before` is not applied by Parallel models. - **Full page text.** `content="full"` returns page text on Exa and joined excerpts on Parallel. It is large, so put it in a separate `read_page` tool rather than the main search tool. - **Different quality or latency.** `["exa/auto", "parallel/advanced"]` favors quality; `["parallel/turbo", "exa/instant"]` favors speed. Every option is listed on the [models page](https://getmux.io/docs/models), and the trade-offs between the two providers are in [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa). | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Cost control Agents search more than you expect. Give each agent its own Mux key with a credit limit so a bad loop stops at a number you chose. This needs a management key (`mux-mgmt-...`), created once in the dashboard: ```python import os from getmux import Mux admin = Mux(api_key=os.environ["MUX_MGMT_KEY"]) secret, key = admin.keys.create("langchain-agent", limit=10, limit_reset="daily") # Put `secret` in MUX_API_KEY for the agent. It is shown only once. ``` ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); const { key } = await admin.keys.create({ name: "langchain-agent", limit: 10, limitReset: "daily" }); // Put `key` in MUX_API_KEY for the agent. It is shown only once. ``` Past the limit, Mux returns `402 key_limit_exceeded` and charges nothing. The limit is checked atomically with the debit, so concurrent agents cannot overshoot it. The dashboard breaks usage down per key. See [managing keys](https://getmux.io/docs/keys) and the guide to [spend limits per API key](https://getmux.io/guides/per-agent-spend-limits). ## Handling 402 and 429 Both `getmux` clients retry `429` and most `5xx` responses twice with backoff and honor `Retry-After`. The ones left for you: | Error | Meaning | In a LangChain tool | |---|---|---| | `402 insufficient_credits` | Account balance is empty | Return an error object; alert someone to [add credits](https://getmux.io/docs/credits). | | `402 key_limit_exceeded` | This key hit its limit for the period | Return an error object; raise the limit or wait for the reset. | | `429 rate_limited` | Over 20 requests per second on one key | Retried by the SDK. If it persists, lower parallelism. | | `429 too_many_concurrent_requests` | Over 50 searches in flight on the account | Retried by the SDK. Queue tool calls across agents. | | `502 all_providers_failed` | Every model in `models` failed | Not retried. Safe to retry once; nothing was charged. | The example returns `MuxError` as data rather than raising. In LangChain 1.x an uncaught exception inside a tool stops the run unless you add the tool error middleware, and even then the model only sees a generic message. Returning `{"error": ..., "results": []}` lets the model tell the user that search is unavailable and answer from what it knows. Branch on `e.code` if you want different handling for billing versus transient errors. See the [errors page](https://getmux.io/docs/errors). ## FAQ ### How is this different from LangChain's built-in search tool integrations? LangChain ships community wrappers for individual search APIs, each with its own argument names, result shape and account. The tool here is a few dozen lines you own, with one response shape and one bill across Exa, Parallel and Keenable, and fallback between them. If you only ever want one provider and already have a contract with them, their native integration is a fine choice. ### Can I use this with LangGraph directly? Yes. `create_agent` returns a compiled LangGraph graph, and the tool itself is a standard `BaseTool`, so you can pass it to a `ToolNode` in your own graph or bind it to a model with `bind_tools`. ### Does `create_agent` limit how many times the tool is called? Not by default. Use the `recursion_limit` in the invoke config, or LangChain's tool call limit middleware, to cap steps. For a hard ceiling on money rather than steps, use the per-key credit limit described above. ### Which model should the agent use? Any chat model with tool calling. The examples use Claude via `langchain-anthropic` / `@langchain/anthropic`; swap the `provider:model` string and install the matching package for OpenAI, Google or others. The tool code does not change. Get a key and run your first search in the [quickstart](https://getmux.io/docs/quickstart). --- # OpenAI Agents SDK web search tool with Mux Build an OpenAI Agents SDK web search tool with @function_tool or tool() that calls Mux for Exa and Parallel results, with provider choice and per-key budgets. Source: https://getmux.io/guides/web-search-tool-openai-agents-sdk An OpenAI Agents SDK web search tool can be either OpenAI's hosted `WebSearchTool`, which runs inside the Responses API, or a function tool you write that calls a search API of your choice. This guide builds the second kind on Mux: a `@function_tool` in Python or `tool()` in TypeScript that searches Exa, Parallel and Keenable on one `MUX_API_KEY`, returns compact results, and plugs into `Agent` and `Runner` like any other tool. It targets `openai-agents` 0.24 (Python) and `@openai/agents` 0.20 (TypeScript). ## Hosted web search vs. a function tool The hosted tool is the easiest option when you want search with no code. A function tool is the better fit when you want control over what gets searched and what comes back. | | Hosted `WebSearchTool` / `webSearchTool()` | Function tool calling Mux | |---|---|---| | Search provider | OpenAI's | You pick: `exa/*`, `parallel/*`, `keenable/*`, with fallback order | | Pricing | Per OpenAI's pricing for the tool, see their pricing page | Provider list price per search, passed through by Mux, for example $0.001 on [`parallel/fast`](https://getmux.io/models/parallel/fast) or $0.007 on [`exa/auto`](https://getmux.io/models/exa/auto) | | Results | Used by the model inside the response, with citations | Raw JSON you can filter, rank, log, cache or trim before the model sees it | | Filters | Domain allow-list and location options | `include_domains`, `exclude_domains`, `published_after`, `published_before`, `max_results`, full page text | | Works with non-OpenAI models | No, it needs the Responses API | Yes, with any model the SDK supports | | Budget | Your OpenAI bill | Per-key USD limit with daily, weekly or monthly reset | For OpenAI's current pricing and options, see [their pricing page](https://openai.com/api/pricing/). The rest of this guide covers the function tool. ## Setup ```bash # Python uv add openai-agents getmux # TypeScript npm install @openai/agents zod getmux export OPENAI_API_KEY="sk-..." export MUX_API_KEY="mux-..." ``` Get a Mux key with a few dollars of credit from the [dashboard](https://getmux.io/dashboard/keys). Versions used: | Package | Version | |---|---| | `openai-agents` (Python) | 0.24.0 | | `@openai/agents` (TypeScript) | 0.20.0 | | `zod` | 4.6.7 (the SDK requires zod 4) | | `getmux` | 0.1.0 | | `pydantic` | 2.14.1 | ## Define the tool In Python, `@function_tool` builds the JSON schema from type hints and reads the docstring for the tool and argument descriptions, so a Pydantic model is only needed for extra constraints. The example uses `Field` for the bounds on `max_results`. In TypeScript, `tool()` takes a Zod schema in `parameters`. Both expose `query`, an optional `max_results` and an optional `include_domains`, and both return title, URL, snippet and publish date only, which keeps the model's context small. ```python # web_search.py from typing import Annotated from agents import function_tool from getmux import AsyncMux, MuxError from pydantic import Field mux = AsyncMux() # reads MUX_API_KEY @function_tool async def web_search( query: str, max_results: Annotated[int, Field(ge=1, le=20)] = 5, include_domains: list[str] | None = None, ) -> dict: """Search the web for current information. Args: query: The search query, phrased like a web search. max_results: How many results to return (1 to 20). include_domains: Only return results from these domains, e.g. ["arxiv.org"]. """ try: res = await mux.search( query, models=["parallel/fast", "exa/auto"], max_results=max_results, include_domains=include_domains, ) except MuxError as e: # Return the failure as data so the agent can explain it rather than abort. return {"error": e.code, "message": e.message, "results": []} return { "model": res.model, "results": [ { "title": r.title, "url": r.url, "snippet": r.snippet, "published_date": r.published_date, } for r in res.results ], } ``` ```ts // web-search.ts import { tool } from "@openai/agents"; import { z } from "zod"; import { Mux, MuxError } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY export const webSearch = tool({ name: "web_search", description: "Search the web for current information. Returns titles, URLs, short snippets and publish dates.", parameters: z.object({ query: z.string().min(1).max(2000).describe("The search query, phrased like a web search"), maxResults: z.number().int().min(1).max(20).nullable().describe("How many results to return (null means 5)"), includeDomains: z .array(z.string()) .nullable() .describe("Only return results from these domains, e.g. ['arxiv.org']"), }), execute: async ({ query, maxResults, includeDomains }) => { try { const res = await mux.search({ query, models: ["parallel/fast", "exa/auto"], maxResults: maxResults ?? 5, includeDomains: includeDomains ?? undefined, }); return { model: res.model, results: res.results.map((r) => ({ title: r.title, url: r.url, snippet: r.snippet, published_date: r.publishedDate, })), }; } catch (e) { if (e instanceof MuxError) { return { error: e.code, message: e.message, results: [] }; } throw e; } }, }); ``` The TypeScript schema uses `.nullable()` rather than `.optional()` for the two optional arguments because the SDK uses strict JSON schema with Zod, where every property is required and "optional" is expressed as nullable. Python handles this for you: defaults and `| None` annotations are converted to a strict schema automatically. `models: ["parallel/fast", "exa/auto"]` tries Parallel first and falls back to Exa if Parallel errors or is at capacity. You pay only for the model that answered; failed attempts are refunded. Mux also enforces `include_domains` after the response, so the filter holds regardless of which provider served the request. Other orders are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents), and the two providers are compared in [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa). ## Run the agent ```python # agent.py import asyncio from agents import Agent, Runner from web_search import web_search agent = Agent( name="Researcher", instructions="Use web_search for current facts and cite the URL of every source you use.", tools=[web_search], ) async def main() -> None: result = await Runner.run( agent, "What changed in the EU AI Act implementation timeline this month?", max_turns=6, ) print(result.final_output) asyncio.run(main()) ``` ```ts // agent.ts import { Agent, run } from "@openai/agents"; import { webSearch } from "./web-search"; const agent = new Agent({ name: "Researcher", instructions: "Use web_search for current facts and cite the URL of every source you use.", tools: [webSearch], }); const result = await run(agent, "What changed in the EU AI Act implementation timeline this month?", { maxTurns: 6, }); console.log(result.finalOutput); ``` `Runner.run` / `run` drives the loop: the model requests `web_search`, the SDK executes it, feeds the JSON back, and repeats until the model produces a final answer or hits `max_turns`. `result.new_items` (Python) or `result.newItems` (TypeScript) contains the tool calls and outputs if you want to build a sources list from the URLs the agent actually fetched. ## Variations - **Recency.** Add a `published_after: str | None` argument and pass it to `mux.search`. Dates are `YYYY-MM-DD` on every provider. `published_before` is not applied by Parallel models. - **Full page text.** `content="full"` returns page text on Exa and joined excerpts on Parallel. Put it in a separate `read_page` tool that takes one URL via `include_domains` plus a specific query, so the main search tool stays small. - **Quality first.** `["exa/auto", "parallel/advanced"]` for deeper research; `["parallel/turbo", "exa/instant"]` for latency-sensitive chat. See the [models page](https://getmux.io/docs/models). | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Cost control `max_turns` caps model turns, not searches, and a single turn can contain several parallel tool calls. The hard stop is a per-key credit limit on the Mux side. Create a dedicated key for the agent with a management key (`mux-mgmt-...`): ```python import os from getmux import Mux admin = Mux(api_key=os.environ["MUX_MGMT_KEY"]) secret, key = admin.keys.create("openai-agent", limit=10, limit_reset="daily") # Put `secret` in MUX_API_KEY for the agent process. It is shown once. ``` ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); const { key } = await admin.keys.create({ name: "openai-agent", limit: 10, limitReset: "daily" }); // Put `key` in MUX_API_KEY for the agent process. It is shown once. ``` Once the key reaches $10 for the day, Mux returns `402 key_limit_exceeded` and charges nothing. The check is atomic with the debit, so parallel tool calls cannot overshoot. One key per agent, or per end customer, also gives you a per-key usage breakdown in the dashboard. See [managing keys](https://getmux.io/docs/keys) and the guide to [spend limits per API key](https://getmux.io/guides/per-agent-spend-limits). ## Handling 402 and 429 The `getmux` clients retry `429` and most `5xx` responses twice with backoff and honor `Retry-After`. What remains: | Error | Meaning | In the tool | |---|---|---| | `402 insufficient_credits` | Account balance is empty | Return an error object; alert an operator to [add credits](https://getmux.io/docs/credits). | | `402 key_limit_exceeded` | This key hit its daily, weekly or monthly limit | Return an error object; raise the limit or wait for the reset. | | `429 rate_limited` | Over 20 requests per second on one key | Retried by the SDK. If it persists, lower concurrency. | | `429 too_many_concurrent_requests` | Over 50 searches in flight on the account | Retried by the SDK. Queue tool calls across agents. | | `502 all_providers_failed` | Every model in `models` failed | Not retried. Safe to retry once; nothing was charged. | The examples return `MuxError` as data. The Agents SDK also has a `failure_error_function` option on `@function_tool` that converts exceptions into a message for the model, but handling `MuxError` explicitly lets you distinguish billing errors (stop and alert) from transient ones (tell the model to try again later). Any other exception still propagates. The full list of codes is on the [errors page](https://getmux.io/docs/errors). ## FAQ ### When should I use OpenAI's hosted web search instead? When you want zero search code, you are already on the Responses API with an OpenAI model, and you do not need to inspect or filter raw results. The hosted tool handles citations for you. Use a function tool when you want to choose the index, filter by domain or date before the model sees anything, log or cache results, or run the same agent on non-OpenAI models. ### Does the tool work with the Realtime or voice agents? Function tools are shared across the SDK, so yes. Keep `max_results` low in voice contexts, since every result is read into the model's context before it speaks. ### Can I run many agents in parallel against one Mux account? Yes, within the limits of 20 requests per second per key and 50 searches in flight per account. Beyond that you get `429` responses that cost nothing and that the SDK retries. Give each agent its own key so one noisy agent cannot consume another's rate budget. ### Is a direct provider account ever the better choice? If you only use one provider at very high volume and have negotiated pricing with them, call them directly, or store that key in Mux with [BYOK](https://getmux.io/docs/byok) so the provider bills you and Mux adds no fee for the first 1,000,000 BYOK requests each month. Get a key and run your first search in the [quickstart](https://getmux.io/docs/quickstart). --- # Parallel search API pricing: turbo, fast, basic, advanced Parallel search API pricing through Mux: per-request and extra-result math for all four tiers, the 50-result cap, full-content behavior and allowed options. Source: https://getmux.io/guides/parallel-search-api-pricing Parallel search API pricing on Mux is per request, with 10 results included and $1.00 per 1,000 results past the tenth. `parallel/turbo` and `parallel/fast` cost $1.00 per 1,000 requests, and `parallel/basic` and `parallel/advanced` cost $5.00. These are Parallel's list prices passed through with no markup, and Mux's only fee is 5.5% when you buy credits. This guide walks through the four tiers, the per-request math with worked examples, monthly cost at volume, the 50-result cap, what `content: "full"` returns on Parallel, the date-filter caveat, and which Parallel options you can pass through. For Parallel's own plans and pricing, see [parallel.ai](https://parallel.ai). For a head to head with Exa on price, latency and content, see [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa). ## The four Parallel tiers on Mux Parallel positions its search API around returning excerpts that are ready to feed to a language model rather than a list of links. On Mux its processors are exposed as four model ids. | Model | What it is | $/1k requests | $/1k extra results | Max results | Typical latency | |---|---|---|---|---|---| | `parallel/turbo` | Fastest tier | 1.00 | 1.00 | 50 | ~200 ms | | `parallel/fast` | Fast, the first stop for Mux `auto` | 1.00 | 1.00 | 50 | ~800 ms | | `parallel/basic` | Standard quality | 5.00 | 1.00 | 50 | ~1 s | | `parallel/advanced` | Highest-quality excerpts | 5.00 | 1.00 | 50 | ~3 s | The tiers pair up on price. `turbo` and `fast` are the same price and differ in speed, and `basic` and `advanced` are the same price and differ in excerpt quality and latency. Because [`parallel/fast`](https://getmux.io/models/parallel/fast) is the first model in Mux's `auto` order, a plain request with no `model` set lands here first. [`parallel/turbo`](https://getmux.io/models/parallel/turbo) is the one to name explicitly when latency matters most. The live table is loaded from `GET /v1/models` and reflects current prices. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## The per-request math Each request includes 10 results. Every result past the tenth costs $0.001 on all four tiers: ``` cost = base_price + max(0, results_returned - 10) × 0.001 ``` You pay for the results Parallel returns, capped at what you asked for. Ask for 30, receive 18, pay for 8 extra results. | Model | 10 results | 25 results | 50 results | |---|---|---|---| | `parallel/turbo` | $0.001 | $0.016 | $0.041 | | `parallel/fast` | $0.001 | $0.016 | $0.041 | | `parallel/basic` | $0.005 | $0.020 | $0.045 | | `parallel/advanced` | $0.005 | $0.020 | $0.045 | On the $1.00 tiers, extra results are the whole story. A 50-result `parallel/turbo` search costs 41 times a 10-result one, and the price difference between `turbo` and `advanced` at 50 results is under 10%. If you need many results per query, pick the tier by quality; if you need few, the cheap tiers are very cheap. ## The 50-result cap Parallel models return at most 50 results. `max_results` accepts up to 100, but Mux caps it to the model's maximum before calling the provider, so a request for 80 results on `parallel/advanced` returns at most 50 and is billed for at most 50. If you need up to 100 results from one call, use an Exa model such as [`exa/auto`](https://getmux.io/models/exa/auto) instead. See [/docs/search](https://getmux.io/docs/search). ## Monthly cost at volume At the default of 10 results per search: | Model | 1,000 searches | 100,000 searches | 1,000,000 searches | |---|---|---|---| | `parallel/turbo` | $1 | $100 | $1,000 | | `parallel/fast` | $1 | $100 | $1,000 | | `parallel/basic` | $5 | $500 | $5,000 | | `parallel/advanced` | $5 | $500 | $5,000 | At 25 results per search, multiply by the 25-result column: a million `parallel/fast` searches at 25 results is $16,000, sixteen times the 10-result figure. Then add the credit purchase fee, 5.5% with a $0.80 minimum. $100 buys $94.50 of credits, $1,000 buys $945. A $500 month of `parallel/basic` costs about $529 at checkout. Purchases are prepaid, between $5 and $10,000 each, and credits do not expire. Full terms are at [/docs/pricing](https://getmux.io/docs/pricing). ## What `content: "full"` returns on Parallel On Parallel, `content: "full"` does not fetch whole pages. Parallel returns excerpts, and Mux joins them together into the `content` field. This is often what you want for an LLM context window, since the excerpts are already selected for relevance, but it is not a page scrape. If you need full page text, use an Exa model, where `content: "full"` returns the complete page. There is no extra Mux charge for `content: "full"` on any provider. ## `published_before` is not applied Parallel's search supports a lower date bound but not an upper one. Mux passes `published_after` through natively, and does not apply `published_before` on Parallel models at all. Results outside your upper bound can therefore appear, and because Parallel already returned them, they are billed. If you need both bounds, route the request to an Exa or Keenable model. Domain filters, by contrast, are passed natively and also re-checked by Mux after the response. ## A worked request Twenty-five results from `parallel/advanced`, with joined excerpts, restricted to a domain and a lower date bound, with `parallel/fast` as the fallback: ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "lithium iron phosphate battery recycling methods", "models": ["parallel/advanced", "parallel/fast"], "max_results": 25, "content": "full", "include_domains": ["nature.com"], "published_after": "2025-01-01" }' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "lithium iron phosphate battery recycling methods", models: ["parallel/advanced", "parallel/fast"], maxResults: 25, content: "full", includeDomains: ["nature.com"], publishedAfter: "2025-01-01", }); console.log(res.model, res.usage.costUsd); // "parallel/advanced", up to 0.02 ``` ```python from getmux import Mux mux = Mux() res = mux.search( "lithium iron phosphate battery recycling methods", models=["parallel/advanced", "parallel/fast"], max_results=25, content="full", include_domains=["nature.com"], published_after="2025-01-01", ) print(res.model, res.cost_usd) # "parallel/advanced", up to 0.02 ``` If Parallel returns fewer than 25 results for the narrow domain filter, `usage.cost_usd` will be lower than $0.020, because extra results are billed on the returned count. ## Parallel options you can pass through Through `provider_options`, Mux allows these Parallel-specific fields: - `max_chars_total`, to bound the total excerpt text across results - `client_model`, to tell Parallel which model will consume the excerpts - `advanced_settings.excerpt_settings`, to tune how excerpts are selected - `advanced_settings.location`, to localize results Fields that would change the bill (processor, result count) or that Mux uses for filtering (domains, dates) are rejected with `400 invalid_request`. The options are forwarded as given; see the [Parallel search reference](https://docs.parallel.ai/api-reference/search-api/search) for accepted values and [/docs/models](https://getmux.io/docs/models) for the allow-list. ## When to go direct to Parallel Mux bills Parallel's list price. If you have a negotiated contract or existing Parallel credits, store your Parallel key with `PUT /v1/provider-keys/parallel` and Parallel bills you directly. The first 1,000,000 BYOK requests per calendar month carry no Mux fee; after that the fee is 5% of list price, or $0.00005 per 10-result `parallel/fast` search. See [/docs/byok](https://getmux.io/docs/byok). If Parallel is your only provider, you don't need fallbacks or per-key spend caps, and your volume is large enough to negotiate, a direct relationship is the simpler choice. Mux is worth it when one key should [fail over to Exa or Keenable](https://getmux.io/guides/search-fallbacks-for-ai-agents), when you want `published_before` and 100-result queries available from the same client, or when [each agent needs its own budget](https://getmux.io/guides/per-agent-spend-limits). ## FAQ ### Is there a markup on Parallel searches through Mux? No. Each Parallel model is billed at Parallel's list price per request. Mux's only charge is 5.5% of each credit purchase, minimum $0.80, deducted from the amount you pay. With your own Parallel key, the first million requests a month carry no Mux fee. ### Why is `parallel/fast` the default for `auto`? Mux's `auto` order is `parallel/fast`, then `exa/auto`, then `keenable/pro`. `parallel/fast` is first because at $0.001 per 10-result search it is the cheapest general-purpose option that accepts long queries, with fallbacks covering the cases where it fails. `deepseek/search` is cheaper at $0.00075 but limits queries to 128 characters, so it is not in the default chain; add it yourself with `models` if your queries are short. ### What do I pay if a Parallel call fails and Mux falls back? Only the model that answered. The failed attempt is refunded, and the response's `attempts` array shows each one. The exception is a 15-second timeout, which keeps the model's base price because the provider has usually completed the search. ### Can I get 100 results from Parallel? No. Parallel models return at most 50 results, and Mux caps `max_results` to 50 before calling them. Exa models go to 100. To run your first Parallel search through Mux, start with the [quickstart](https://getmux.io/docs/quickstart). --- # Search API fallback for AI agents with Mux How to set up a search API fallback for AI agents with Mux's models list, so a provider outage, rate limit or timeout never stops a run mid-task. Source: https://getmux.io/guides/search-fallbacks-for-ai-agents A search API fallback is an ordered list of search providers that a request walks through until one answers. In Mux you pass that list as `models`, up to five model ids, and the gateway tries each in order, refunds failed attempts and returns the first success along with an `attempts` array that shows what happened. An agent wired to a single provider stops when that provider has a bad minute; an agent with a fallback list keeps going and pays only for the model that answered. ## Why one search provider is a single point of failure An agent that calls a web search tool in a loop depends on that tool in a way a human user never does. A person who sees an error retries in a few seconds. An agent mid-plan either crashes the run, hallucinates around the gap, or burns retries against a provider that is still down. The failure modes are ordinary: - **Provider 5xx.** Every search API has outages and partial outages. During one, every request from your agent fails until the provider recovers. - **Rate limits.** A burst of parallel tool calls, or a few agents sharing one provider key, can hit the provider's per-second or per-minute cap. Retrying harder makes it worse. - **Timeouts.** A slow upstream turns a 1 second tool call into a 15 second one, and an agent framework with its own timeout gives up first. - **Regional problems.** A provider can be fine from one region and unreachable from another. The usual fix is to integrate two or three search APIs, write a retry wrapper, normalize three response shapes, and hold three billing relationships. Mux does that once, behind one endpoint, with one key and one bill. The response shape is identical no matter which provider answered, so your agent's parsing code does not change when the fallback kicks in. Failure handling is one of the requirements covered in the [buyer's guide to web search APIs for AI agents](https://getmux.io/compare/web-search-apis-for-ai-agents). ## How the `models` list works `POST /v1/search` accepts either `model` (one id) or `models` (an ordered list of up to five). When `models` is set it takes precedence. The rules: | Behavior | What Mux does | |---|---| | Order | Tries each model in the order given, stops at the first success. | | Failed attempts | The price was reserved before the call and is refunded when the call fails. You pay for the model that answered. | | Unaffordable models | A model whose price would exceed your balance or the key's credit limit is skipped, not called and not charged. A cheaper model later in the list can still run. | | Timeouts | A provider that has not answered after 15 seconds is abandoned and the next model is tried. Because the provider has almost always finished the search by then, that attempt keeps the model's base price, shown as `cost_usd` on its entry in `attempts`. | | All fail | You get `502 all_providers_failed` with the full `attempts` array. Nothing is charged, except any timed-out attempt. | | Observability | Every response includes `attempts`, one entry per model tried, with `status` of `ok`, `error` or `skipped`. | If you omit both fields, Mux uses `auto`, which is a built-in fallback list: `parallel/fast`, then `exa/auto`, then `keenable/pro`. That order favors the cheapest model first and still covers three of the four providers (DeepSeek is not in the built-in list). The Mux SDKs retry `429` and `5xx` responses twice with backoff, but not `502 all_providers_failed`, because the gateway has already tried everything you allowed. That error is safe to retry yourself since you were not charged. ## Recommended orders for three goals Prices below are list prices per 1,000 requests with 10 results each. See the live table on [/pricing](https://getmux.io/pricing) and the per-model pages such as [/models/parallel/fast](https://getmux.io/models/parallel/fast). | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ### Cheapest first Start with `parallel/fast` at $1 per 1,000, fall back to [`keenable/pro`](https://getmux.io/models/keenable/pro) at $4, and keep [`exa/auto`](https://getmux.io/models/exa/auto) at $7 as the last resort. How the three providers differ beyond price is in [Exa vs Parallel vs Keenable](https://getmux.io/compare/exa-vs-parallel-vs-keenable). Most requests cost $0.001, and the expensive model only runs when both cheaper ones are down. ```bash curl https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "postgres connection pooling", "models": ["parallel/fast", "keenable/pro", "exa/auto"]}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "postgres connection pooling", models: ["parallel/fast", "keenable/pro", "exa/auto"], }); console.log(res.model, res.usage.costUsd); ``` ```python from getmux import Mux mux = Mux() res = mux.search( "postgres connection pooling", models=["parallel/fast", "keenable/pro", "exa/auto"], ) print(res.model, res.cost_usd) ``` ### Quality first Lead with `exa/auto`, which is Exa's general-purpose mode, then `parallel/advanced` for its high-quality excerpts, then `keenable/pro`. Use this for research agents where a weaker result set costs more in model tokens than the search does. ```ts const res = await mux.search({ query: "peer-reviewed studies on intermittent fasting and insulin sensitivity", models: ["exa/auto", "parallel/advanced", "keenable/pro"], maxResults: 20, content: "full", }); ``` ```python res = mux.search( "peer-reviewed studies on intermittent fasting and insulin sensitivity", models=["exa/auto", "parallel/advanced", "keenable/pro"], max_results=20, content="full", ) ``` Note that `content: "full"` returns page text on Exa and joined excerpts on Parallel, but `content` is `null` on Keenable. If your agent needs page text, put Keenable last or leave it out. ### Latency first For interactive agents, start with `parallel/turbo` at about 200 ms typical, then `exa/instant` at about 300 ms, then `keenable/pro` at about 700 ms. ```ts const res = await mux.search({ query: "weather api with hourly forecast", models: ["parallel/turbo", "exa/instant", "keenable/pro"], maxResults: 5, }); ``` ```python res = mux.search( "weather api with hourly forecast", models=["parallel/turbo", "exa/instant", "keenable/pro"], max_results=5, ) ``` A good rule for any order: put at least two different providers in the list. Two Exa variants protect you against a bad model, not against an Exa outage. ## Reading `attempts` in your logs Log `res.model` and `res.attempts` on every search. A normal response has a single `ok` entry. A response that fell back looks like this: ```json { "model": "keenable/pro", "usage": { "cost_usd": 0.004, "results": 10, "latency_ms": 712, "byok": false }, "attempts": [ { "model": "parallel/fast", "source": "platform", "status": "error", "error": "parallel 503: service unavailable" }, { "model": "keenable/pro", "source": "platform", "status": "ok" } ] } ``` Things to watch for: - **`status: "error"`** means the provider was called and failed. It was refunded unless `cost_usd` is present, which marks a timeout that kept the base price. - **`status: "skipped"`** means the model was never called. The `error` field says why, usually insufficient credits or a key limit. If you see this often, raise the key's limit or move a cheaper model earlier in the list. How limits interact with fallbacks is covered in [spend limits per API key](https://getmux.io/guides/per-agent-spend-limits). - **The last entry** is always the model that served the request, and matches the top-level `model` field and the `x-mux-model` response header. - **A rising fallback rate** for one provider is an early warning that it is degraded. Counting `attempts` entries with `status: "error"` by model over a window gives you a free provider health dashboard. The same information appears per request on the activity page of the [dashboard](https://getmux.io/dashboard) and from `GET /v1/activity`, where each failed upstream attempt is logged as its own row with its error. ```ts for (const a of res.attempts) { if (a.status !== "ok") console.warn("search fallback", a.model, a.status, a.error); } ``` ```python for a in res.attempts: if a["status"] != "ok": print("search fallback", a["model"], a["status"], a.get("error")) ``` ## FAQ ### Does a fallback cost more than a single-provider request? No. Each attempt's price is reserved before the call and refunded if it fails, so you pay for the model that succeeded. The one exception is a timed-out attempt, which keeps that model's base price because the provider has normally completed the search by then. ### What happens if every model in the list fails? You get `502 all_providers_failed` with the `attempts` array in the error body, and your balance is unchanged apart from any timed-out attempt. The SDKs do not retry this status automatically, but it is safe for you to retry. ### Can I use `models` with my own provider keys? Yes. With [BYOK](https://getmux.io/docs/byok), a request to a provider you have connected runs on your key, and `attempts` shows `source: "byok"` for it. If your key fails, Mux falls back to its own account for that provider at list price unless you set `always_use: true`. ### How many models can I list? Up to five. Past three or four you get diminishing returns, since Mux currently routes to four providers. A list that covers more than one provider is the strongest guard against an outage. Set `MUX_API_KEY` and send your first fallback request in a minute with the [quickstart](https://getmux.io/docs/quickstart). --- # Spend limits per API key for agents and tenants How to set spend limits per API key in Mux, so every agent, customer or environment has its own USD budget that resets on a schedule and cannot be overshot. Source: https://getmux.io/guides/per-agent-spend-limits Mux lets you set spend limits per API key: a USD `limit` and an optional `limit_reset` of `daily`, `weekly` or `monthly`. Issue one key per agent, customer or environment with a management key, and each one gets its own hard budget. The limit is checked in the same atomic step as the debit, so a runaway loop or a burst of concurrent requests cannot push a key past its cap, and a blocked request costs nothing. ## Why one key per agent A single shared search key gives you one number: total spend. It does not tell you which agent went into a loop, which customer is heavy, or whether staging is quietly eating production's budget. Worse, when something misbehaves, your only kill switch takes everything down with it. One key per unit of accountability fixes all three problems: - **Per agent:** a research agent that retries searches in a loop stops at its own cap instead of draining the account. What a loop can cost per hour on each model is worked out in [web search cost per agent run](https://getmux.io/guides/web-search-cost-per-agent-run). - **Per customer:** a tenant's usage is visible on its own line, and you can cut off one tenant without touching the others. - **Per environment:** development and staging keys get small daily limits, production gets a monthly one. Keys are created and administered with a management key (`mux-mgmt-...`), which cannot run searches itself. API keys (`mux-...`) can only search and read their own usage. Details are on [Managing keys](https://getmux.io/docs/keys). ## Creating a key with a limit `POST /v1/keys` takes an optional `name`, a `limit` in USD from 0 to 1,000,000, a `limit_reset` of `daily`, `weekly` or `monthly`, and an optional `expires_at`. The secret is returned once, on creation, and stored hashed. ```bash curl https://api.getmux.io/v1/keys \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "research-agent", "limit": 10, "limit_reset": "daily"}' ``` ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); const { key, data } = await admin.keys.create({ name: "research-agent", limit: 10, limitReset: "daily", }); // key -> "mux-..." store it now; it is never returned again // data -> key object with id, limit, limitRemaining, limitReset ``` ```python from getmux import Mux admin = Mux(api_key=os.environ["MUX_MGMT_KEY"]) secret, key = admin.keys.create("research-agent", limit=10, limit_reset="daily") # secret -> "mux-..." store it now; it is never returned again # key -> ApiKey with id, limit, limit_remaining, limit_reset ``` Reset windows are in UTC: `daily` at 00:00, `weekly` at 00:00 on Monday, `monthly` at 00:00 on the 1st. With no `limit_reset`, the limit is a lifetime cap. A limit of `0` creates a key that can't spend, which is useful as a placeholder you raise later. ## How enforcement works The limit check and the balance check run together, in the same step that reserves the request's price. That matters for agents because they fail in bursts: fifty concurrent tool calls arriving at once are each checked against what is left, and none of them can slip past the cap between a read and a write. | Event | Effect on the key's limit | |---|---| | Successful search | Debited at the model's list price. | | Provider call fails and is refunded | The amount is returned to the limit as well as to your balance. | | Request would exceed the limit | Rejected with `402 key_limit_exceeded`. Nothing is charged. | | Fallback list with a too-expensive model | That model is skipped; a cheaper one later in `models` can still run. See [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). | | BYOK fees | Count against the key's limit like any other charge, and are refunded if the call fails. | Lowering a limit below what the key has already spent does not refund anything; the key is blocked until its window resets. ## Handling `402 key_limit_exceeded` When a key is out of budget, the search returns `402` with this body. The `needed_usd` field is the price of the cheapest model that was blocked. ```json { "error": { "code": "key_limit_exceeded", "message": "This key's credit limit would be exceeded.", "needed_usd": 0.001 } } ``` Do not retry it. Nothing changes until the window resets, the limit is raised, or a different key is used. The SDKs throw `MuxError` and never retry `4xx`. The right handling depends on who the key belongs to: ```ts import { Mux, MuxError } from "getmux"; const mux = new Mux({ apiKey: tenantKey }); try { const res = await mux.search({ query, models: ["parallel/fast", "exa/auto"] }); return res.results; } catch (e) { if (e instanceof MuxError && e.code === "key_limit_exceeded") { // Tell the agent its search budget is spent; let it finish with what it has. return []; } throw e; } ``` ```python from getmux import Mux, MuxError mux = Mux(api_key=tenant_key) try: res = mux.search(query, models=["parallel/fast", "exa/auto"]) results = res.results except MuxError as e: if e.code == "key_limit_exceeded": # Tell the agent its search budget is spent; let it finish with what it has. results = [] else: raise ``` For a customer-facing product, surface the state to the tenant ("search budget used for this month") and offer an upgrade path that calls `keys.update` with a higher `limit`. The examples lead with [`parallel/fast`](https://getmux.io/models/parallel/fast) at $0.001 per search so a small limit goes a long way, and fall back to [`exa/auto`](https://getmux.io/models/exa/auto) at $0.007 only if Parallel fails. ## Reading `limit_remaining` and usage by key An agent can check its own budget before starting expensive work with `GET /v1/key`, which works with the API key itself. The response is the key object, including `limit`, `limit_remaining`, `usage` (lifetime) and `usage_period` (current window). ```bash curl https://api.getmux.io/v1/key -H "Authorization: Bearer $MUX_API_KEY" ``` ```ts const me = await mux.key(); if (me.limitRemaining !== null && me.limitRemaining < 0.05) { console.warn("search budget nearly spent", me.limitRemaining); } ``` ```python me = mux.key() if me.limit_remaining is not None and me.limit_remaining < 0.05: print("search budget nearly spent", me.limit_remaining) ``` From the management side, `GET /v1/keys/{id}` returns the same object for any key, and `GET /v1/usage` aggregates spend by day, by model and by key over a window of up to 90 days. The `by_key` list is sorted by cost, highest first, so the top entry is your noisiest agent or tenant. Deleted keys still appear with their past usage. ```ts const usage = await admin.usage(30); for (const row of usage.byKey) console.log(row.name, row.requests, row.costUsd); ``` ```python usage = admin.usage(30) for row in usage["by_key"]: print(row["name"], row["requests"], row["cost_usd"]) ``` The [dashboard](https://getmux.io/dashboard) shows the same breakdown by key and by model, plus a per-request activity log with the key name on each row. ## Disabling and rotating a key Disabling is a reversible kill switch. Deleting is permanent. Both take effect immediately on the server that handled the change and within about 15 seconds everywhere. ```bash # kill switch curl -X PATCH https://api.getmux.io/v1/keys/key_Hx2mQp9aLwT4kZ1v \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{"disabled": true}' # permanent curl -X DELETE https://api.getmux.io/v1/keys/key_Hx2mQp9aLwT4kZ1v \ -H "Authorization: Bearer $MUX_MGMT_KEY" ``` ```ts await admin.keys.update(keyId, { disabled: true }); // kill switch await admin.keys.update(keyId, { disabled: false }); // back on await admin.keys.delete(keyId); // permanent ``` ```python admin.keys.update(key_id, disabled=True) # kill switch admin.keys.update(key_id, disabled=False) # back on admin.keys.delete(key_id) # permanent ``` A disabled key returns `401 key_disabled`; an expired one returns `401 key_expired`. To rotate, create the replacement first with the same `name` and `limit`, deploy it, then delete the old key. Because the secret is only shown once, rotation is the only way to recover from a lost or leaked key. ## Pattern: multi-tenant SaaS with a key per tenant Give each tenant a key whose `name` is the tenant id. The name then shows up in usage reports and the activity log, so you never need a lookup table from key id to customer. Keep the mapping from tenant id to key id in your own database so you can raise, disable or delete the right key. ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); export async function provisionTenant(tenantId: string, plan: "free" | "pro") { const { key, data } = await admin.keys.create({ name: tenantId, limit: plan === "pro" ? 50 : 2, limitReset: "monthly", }); await db.tenants.update(tenantId, { muxKeyId: data.id, muxKey: encrypt(key) }); } export async function upgradeTenant(tenantId: string) { const { muxKeyId } = await db.tenants.get(tenantId); await admin.keys.update(muxKeyId, { limit: 50 }); } export async function suspendTenant(tenantId: string) { const { muxKeyId } = await db.tenants.get(tenantId); await admin.keys.update(muxKeyId, { disabled: true }); } ``` ```python from getmux import Mux admin = Mux(api_key=os.environ["MUX_MGMT_KEY"]) def provision_tenant(tenant_id: str, plan: str): secret, key = admin.keys.create( tenant_id, limit=50 if plan == "pro" else 2, limit_reset="monthly", ) db.tenants.update(tenant_id, mux_key_id=key.id, mux_key=encrypt(secret)) def upgrade_tenant(tenant_id: str): admin.keys.update(db.tenants.get(tenant_id).mux_key_id, limit=50) def suspend_tenant(tenant_id: str): admin.keys.update(db.tenants.get(tenant_id).mux_key_id, disabled=True) ``` At request time, build the client with the tenant's key, not your management key. The tenant's key can see only its own usage and activity, so even a leaked tenant key cannot read what other tenants are doing or open a credit purchase. ## FAQ ### Can a key overshoot its limit under concurrent load? No. The limit check happens in the same atomic step as the debit, so parallel requests are each checked against the amount that is actually left. A request that would exceed the limit is rejected before any provider is called. ### Does a blocked request cost anything? No. A `402 key_limit_exceeded` response is free, and so are requests blocked by insufficient credits or rate limits. Failed provider calls are refunded to both your balance and the key's limit. ### What is the difference between a key limit and my credit balance? The balance is one pool shared by all keys on the account. A key limit is a cap on how much of that pool one key may use in its window. A key can be blocked by its own limit while the account still has plenty of credit, and vice versa. ### Can I change a limit without reissuing the key? Yes. `PATCH /v1/keys/{id}` changes `limit`, `limit_reset`, `name` and `disabled` in place. The secret stays the same. Only `expires_at` is fixed at creation. Create your first key and run a search in a minute with the [quickstart](https://getmux.io/docs/quickstart). --- # Web search cost for AI agents: a budgeting guide How to budget web search cost for AI agents on Mux: monthly cost per model at 1k to 1M searches, fallback billing, credit fees and per-key spend limits. Source: https://getmux.io/guides/web-search-cost-per-agent-run Web search cost for AI agents comes down to three numbers: price per search, searches per run, and runs per month. On Mux a 10-result search costs between $0.001 (`parallel/turbo`, `parallel/fast`) and $0.012 (`exa/deep`), so an agent that makes 20 searches per run costs between two cents and twenty-four cents in search per run. The hard part is not the arithmetic but keeping a looping agent from running the arithmetic a million times. This guide covers monthly cost for every Mux model, what you pay when a request falls back, how the credit purchase fee changes the bill, the per-key limits that stop a runaway agent, and how to measure what you spent. ## Monthly cost by model All prices are the providers' list prices with no per-request markup. The table assumes 10 results per search, which is what each request includes. | Model | $/search | 1,000 | 10,000 | 100,000 | 1,000,000 | |---|---|---|---|---|---| | `deepseek/search` | $0.00075 | $0.75 | $7.50 | $75 | $750 | | `parallel/turbo` | $0.001 | $1 | $10 | $100 | $1,000 | | `parallel/fast` | $0.001 | $1 | $10 | $100 | $1,000 | | `exa/instant` | $0.004 | $4 | $40 | $400 | $4,000 | | `keenable/pro` | $0.004 | $4 | $40 | $400 | $4,000 | | `keenable/realtime` | $0.004 | $4 | $40 | $400 | $4,000 | | `parallel/basic` | $0.005 | $5 | $50 | $500 | $5,000 | | `parallel/advanced` | $0.005 | $5 | $50 | $500 | $5,000 | | `exa/fast` | $0.007 | $7 | $70 | $700 | $7,000 | | `exa/auto` | $0.007 | $7 | $70 | $700 | $7,000 | | `exa/deep` | $0.012 | $12 | $120 | $1,200 | $12,000 | Extra results beyond 10 cost $0.001 each on Exa and Parallel models, billed on what the provider returns and capped at what you asked for. Keenable and DeepSeek charge nothing for extra results, and DeepSeek charges nothing for a search that returns no results. An agent pulling 25 results per `exa/auto` search pays $0.022 per search, so the 100,000 column becomes $2,200 rather than $700. After model choice, result count is the biggest lever on your bill. The per-tier tables at 10, 25 and more results are in [Exa pricing explained](https://getmux.io/guides/exa-pricing-explained) and [Parallel search API pricing](https://getmux.io/guides/parallel-search-api-pricing). The live price table below is loaded from `GET /v1/models` and wins if this page ever drifts. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | Mux's `auto` model tries [`parallel/fast`](https://getmux.io/models/parallel/fast), then [`exa/auto`](https://getmux.io/models/exa/auto), then [`keenable/pro`](https://getmux.io/models/keenable/pro), so an agent that never sets `model` mostly pays the `parallel/fast` rate and only pays more when Parallel fails. ## From per-search to per-run Count the searches a typical run makes, including retries and sub-agents, and multiply. A research agent that plans, searches 15 times on `parallel/fast`, then verifies 3 claims on `exa/auto` costs: ``` 15 × $0.001 + 3 × $0.007 = $0.036 per run ``` At 10,000 runs a month that is $360. The same agent on `exa/auto` throughout would be 18 × $0.007 = $0.126 per run, or $1,260 a month. A cheap model for breadth and a higher-quality model for final checks is usually the biggest saving that needs no change to the agent's logic. ## How fallbacks affect cost When you pass a `models` list, Mux tries each in order and you pay only for the model that answered. A failed attempt is refunded before the next one starts. With `["parallel/fast", "exa/auto"]`, a run where Parallel errors and Exa answers costs $0.007, not $0.008. The exception is a provider timeout. If a provider has not answered after 15 seconds, the attempt is marked as an error but keeps the model's base price, because the provider has usually completed and billed the search by then. Only the extra-results portion is refunded. In the same example, a Parallel timeout followed by an Exa success costs $0.001 + $0.007 = $0.008. The response's `attempts` array shows a `cost_usd` on any timed-out attempt so you can see it. If every model fails you get `502 all_providers_failed` and pay nothing except for any timed-out attempts. Requests rejected for validation, authentication, rate limiting, insufficient credits or a key limit are never charged. For budgeting, treat the most expensive model in your list as the worst case per request. The average stays close to the first model's price unless that provider is having a bad day. Recommended orders and how to read the `attempts` array are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). ## How the credit purchase fee works Mux is prepaid. The only Mux fee is charged when you buy credits: 5.5% of the amount, with a minimum of $0.80, taken out of what you pay. | You pay | Fee | Credits added | |---|---|---| | $5 | $0.80 | $4.20 | | $10 | $0.80 | $9.20 | | $50 | $2.75 | $47.25 | | $100 | $5.50 | $94.50 | | $1,000 | $55.00 | $945.00 | The minimum means the fee is $0.80 on anything up to about $14.55 and 5.5% above that, so small frequent top-ups are the most expensive way to buy. To land a specific credit amount, divide by 0.945: $700 of `exa/auto` searches needs a purchase of about $741. Purchases run from $5 to $10,000 each, so a $12,000 month of `exa/deep` needs two. There is no subscription and credits do not expire. Details at [/docs/pricing](https://getmux.io/docs/pricing). If you bring your own provider key, the first 1,000,000 BYOK requests per calendar month carry no Mux fee at all, and the provider bills you directly. See [/docs/byok](https://getmux.io/docs/byok). ## Per-key limits stop runaway loops An agent stuck in a loop is the main way a search budget goes wrong. Mux's rate limit of 20 requests per second per key bounds the damage, but at that rate `parallel/fast` still burns $72 an hour and `exa/auto` burns $504 an hour. A credit limit on the key is what actually stops it. Each API key can carry a USD `limit` from 0 to 1,000,000 and a `limit_reset` of `daily`, `weekly` or `monthly`. The check happens atomically with the debit, so concurrent requests cannot push past it. A request that would exceed the limit is rejected with `402 key_limit_exceeded` and nothing is charged. With a `models` list, a model that does not fit under the remaining limit is skipped and a cheaper one later in the list can still run. Refunded calls return their amount to the limit. Create one key per agent, customer or environment with a management key. The full pattern, including a key per tenant, is in [spend limits per API key](https://getmux.io/guides/per-agent-spend-limits). ```bash curl https://api.getmux.io/v1/keys \ -H "Authorization: Bearer $MUX_MGMT_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "research-agent-prod", "limit": 25, "limit_reset": "daily"}' ``` ```ts import { Mux } from "getmux"; const admin = new Mux({ apiKey: process.env.MUX_MGMT_KEY }); const { key, data } = await admin.keys.create({ name: "research-agent-prod", limit: 25, limitReset: "daily", }); // store `key` now; it is only returned once ``` ```python from getmux import Mux admin = Mux(api_key="mux-mgmt-...") secret, key = admin.keys.create("research-agent-prod", limit=25, limit_reset="daily") # store `secret` now; it is only returned once ``` A $25 daily cap on `parallel/fast` is 25,000 searches a day and a hard ceiling of $775 in a 31-day month. Reset windows are in UTC. The same management key can lower a limit, disable a key as a kill switch, or delete it; see [/docs/keys](https://getmux.io/docs/keys). ## Measuring what you spent Every search response carries `usage.cost_usd`, the settled price of that request after extra results and refunds. Log it next to your run id and you have exact per-run cost. ```ts const res = await mux.search({ query, models: ["parallel/fast", "exa/auto"] }); runCost += res.usage.costUsd; ``` ```python res = mux.search(query, models=["parallel/fast", "exa/auto"]) run_cost += res.cost_usd ``` For totals, `GET /v1/usage?days=30` returns spend and request counts per day, per model and per key. The [dashboard](https://getmux.io/dashboard) shows the same breakdowns, a per-request activity log, and a ledger of purchases, usage and refunds. An agent can check its remaining budget before expensive work with `GET /v1/key`, which returns `limit_remaining`. The endpoints are documented at [/docs/credits](https://getmux.io/docs/credits). ## FAQ ### What does one web search cost for an AI agent on Mux? Between $0.001 and $0.012 for 10 results, depending on the model, plus $0.001 per result beyond 10 on Exa and Parallel. The providers' list prices are passed through unchanged; the only Mux fee is 5.5% when you buy credits. ### How do I stop an agent from spending too much? Give it its own API key with a `limit` and a `limit_reset` of `daily`, `weekly` or `monthly`. Requests that would exceed the limit are rejected with `402 key_limit_exceeded` and cost nothing, and the limit is enforced atomically so concurrency cannot leak past it. ### Do I pay twice when a request falls back to another provider? No. Failed attempts are refunded and you pay for the model that answered. The exception is a 15-second provider timeout, which keeps that model's base price because the provider usually completed the search. ### Is there a monthly subscription? No. Mux is prepaid with a $5 minimum purchase, a 5.5% purchase fee with an $0.80 minimum, and credits that do not expire. There are no per-seat or platform charges. Start with the [quickstart](https://getmux.io/docs/quickstart) to get a key and run your first search. --- # Brave Search API alternatives for AI agents Neutral guide to Brave Search API alternatives, covering Exa, Parallel, Keenable, DeepSeek, Tavily, Serper and SerpApi, with prices per 1,000 requests and when to stay on Brave. Source: https://getmux.io/compare/brave-search-api-alternatives The main Brave Search API alternatives for agent builders are Exa, Parallel, Keenable and DeepSeek, which are search APIs built for LLM consumption with per-request pricing from $0.75 to $12 per 1,000; Tavily, which bundles search with extract and crawl; and Serper or SerpApi, which return Google results as JSON. Brave's own Search plan is $5 per 1,000 requests with a 20-result cap and no page text, so the reasons to look elsewhere are usually a lower price at volume, more results per call, full page content, or a semantic index. Mux routes to the first four behind one key, which makes them easy to try side by side. Brave is a good product and a reasonable default. This page is for the cases where one of its constraints is the thing standing in your way. ## What Brave Search API offers Brave runs an independent web index, which is its main selling point: results that do not come from Google or Bing. As of October 2026 its [API page](https://brave.com/search/api/) lists a Search plan at $5 per 1,000 requests with $5 in free credit applied each month and a 50 queries per second limit, covering web, news, images and videos with extra snippets, Goggles for custom reranking, and schema-enriched results. An Answers plan at $4 per 1,000 requests plus $5 per million tokens returns a grounded, cited answer through an OpenAI-compatible interface at 2 queries per second. Enterprise terms are custom. A card is requested on signup, and billing is prepaid credit rather than a subscription. Per the [query reference](https://api-dashboard.search.brave.com/app/documentation/web-search/query), a web search returns at most 20 results per page with up to 9 further pages by offset, supports freshness filters for the last day, week, month, year or a custom date range, takes a country and language, and offers up to 5 extra snippets per result. Site restrictions go in the query as `site:` operators rather than a parameter. There is no full page text option on the web search endpoint; Brave points machine consumers to a separate LLM Context endpoint, whose price is not listed on the API page. Staying on Brave makes sense when: - You specifically want an independent index that is neither Google-derived nor neural. - 20 results per call and snippets are enough, and $5 per 1,000 fits your budget. - You use Goggles, the Answers plan, or Brave's image, video or news verticals. - You want a vendor with a 50 queries per second ceiling on the standard plan. ## The alternatives ### Parallel Parallel is the price leader. Through Mux, [`parallel/fast`](https://getmux.io/models/parallel/fast) and [`parallel/turbo`](https://getmux.io/models/parallel/turbo) are $1 per 1,000 requests, one fifth of Brave's Search plan, with `turbo` typically answering in about 200 ms. The `basic` and `advanced` tiers at $5 match Brave's price and return higher-quality excerpts chosen for an LLM. Results max out at 50, extra results beyond 10 cost $1 per 1,000, and `published_before` is not applied. Direct access and a generous free credit are at [parallel.ai/pricing](https://parallel.ai/pricing); the tier breakdown is in [Parallel search API pricing](https://getmux.io/guides/parallel-search-api-pricing). ### Exa Exa is a neural search engine and the choice when semantic queries or whole pages matter. [`exa/instant`](https://getmux.io/models/exa/instant) is $4 per 1,000, [`exa/auto`](https://getmux.io/models/exa/auto) $7 and `exa/deep` $12, each with up to 100 results and full page text inline with `content: "full"`. Against Brave that is five times the result cap and page text without a second call, at a price from slightly below to above Brave's. Exa also sells a contents endpoint directly at [exa.ai](https://exa.ai/pricing). ### Keenable Keenable runs its own crawler and index, so like Brave it is an independent source. Through Mux, [`keenable/pro`](https://getmux.io/models/keenable/pro) and `keenable/realtime` are a flat $4 per 1,000 requests with no charge for results beyond 10, up to 50. That makes a 50-result Keenable call cost $0.004, which is the cheapest way to get a wide result set from any provider here. It returns snippets only, no page text, which puts it closest to Brave in output shape. See the [Keenable search API guide](https://getmux.io/guides/keenable-search-api). ### DeepSeek DeepSeek's search API is the cheapest option in this list at $0.75 per 1,000 requests through Mux as `deepseek/search`, flat for up to 50 results, with empty answers not billed. Each result carries a long summary written for a model, and `content: "full"` returns page text. The hard constraint is a 128-character query limit, including operators, so it suits short keyword-style queries rather than long natural-language ones. Direct docs are at [api-docs.deepseek.com](https://api-docs.deepseek.com/guides/search/). ### Tavily Tavily is search plus extract, map and crawl, aimed at agent frameworks. As of October 2026 its [pricing page](https://www.tavily.com/pricing) lists 1,000 free credits a month, pay-as-you-go at $0.008 per credit, and a $30 Project plan with 4,000 credits; a [basic search is 1 credit and an advanced search 2](https://docs.tavily.com/documentation/api-credits), so $8 or $16 per 1,000 at the pay-as-you-go rate. It caps results at 20, like Brave, but can return raw page content on the search call. Pick Tavily over Brave when you want crawl and extract from the same vendor. The wider field is covered in [Tavily alternatives](https://getmux.io/compare/tavily-alternatives). ### Serper and SerpApi If what you actually want is Google's results, these two return them as JSON. Serper's [site](https://serper.dev) advertises 2,500 free queries with no card and results in 1 to 2 seconds across search, news, images, places, scholar and more; its public pricing page was not available when we checked in October 2026, so confirm per-query rates in your account. SerpApi's [pricing page](https://serpapi.com/pricing) lists monthly plans from $25 for 1,000 searches to $2,750 for 500,000, which is $25 down to $5.50 per 1,000, plus a 250-search free plan. Both are a different category from Brave: Google-derived rather than independent, and priced for SERP features rather than agent retrieval. The [SerpApi alternatives](https://getmux.io/compare/serpapi-alternatives) page goes deeper on that category. ## Comparison at a glance | Provider | Index | Price per 1k (cheapest) | Max results | Page text | Via Mux | |---|---|---|---|---|---| | Brave Search | own, independent | $5 | 20 per page | no | no | | Parallel | own | $1 | 50 | excerpts | yes | | Exa | own, neural | $4 | 100 | full, inline | yes | | Keenable | own | $4 flat | 50 | no | yes | | DeepSeek | own | $0.75 flat | 50 | full, inline | yes | | Tavily | own | $8 pay-as-you-go | 20 | raw content or extract | no | | Serper | Google | see account | see docs | no | no | | SerpApi | Google and others | $5.50 to $25 by plan | per engine | no | no | Prices are list prices as of October 2026; Mux passes them through with no per-request markup and charges a 5.5% fee (minimum $0.80) when you buy credits. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## How to compare them on your own queries The usual obstacle is that each vendor needs its own account, adapter and bill before you can see a single result. Through Mux, Parallel, Exa, Keenable and DeepSeek share one endpoint, so comparing them is a loop over model ids. ```bash curl -X POST https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "independent web index search api", "model": "keenable/pro", "max_results": 20}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); for (const model of ["parallel/fast", "keenable/pro", "exa/auto", "deepseek/search"]) { const res = await mux.search({ query: "independent web index search api", model, maxResults: 20 }); console.log(model, res.usage.latencyMs, res.usage.costUsd, res.results.length); } ``` ```python from getmux import Mux mux = Mux() for model in ["parallel/fast", "keenable/pro", "exa/auto", "deepseek/search"]: res = mux.search("independent web index search api", model=model, max_results=20) print(model, res.cost_usd, len(res.results)) ``` Once you have a favorite, keep the others behind it in a `models` list so a provider outage does not stop your agent. The mechanics are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). If you are replacing Brave because of a rate ceiling, note that Mux allows 20 requests per second per key and 50 searches in flight per account, which is below Brave's 50 queries per second on paper; spread load over several keys or talk to the vendor directly for more. ## When Mux is the wrong choice Mux does not route to Brave, Tavily, Serper or SerpApi, so if one of those is the right fit, go direct. It also does not expose Brave's Answers plan, LLM Context endpoint or Goggles, or any vendor's crawl or extract product. And at very high sustained volume on one provider, a negotiated contract may beat list price; with [BYOK](https://getmux.io/docs/byok) you can keep your own vendor key and still use the fallback chain. ## FAQ ### What is the cheapest Brave Search API alternative? By list price as of October 2026, `deepseek/search` at $0.75 per 1,000 requests through Mux, with a 128-character query limit, then `parallel/fast` and `parallel/turbo` at $1. Brave's Search plan is $5 per 1,000. ### Which alternatives have their own index like Brave? Keenable, Exa, Parallel, DeepSeek and Tavily all operate their own indexes. Serper and SerpApi return Google's results. If independence from Google is the requirement, Keenable is closest in shape to Brave: own index, snippets only, flat price. ### Which alternatives return more than 20 results? Exa returns up to 100 per request. Parallel, Keenable and DeepSeek return up to 50. Brave returns 20 per page and lets you page through up to 10 pages with the offset parameter, each page billed as a request. ### Can I get full page text from any of these? Exa and DeepSeek return it inline through Mux with `content: "full"`. Tavily returns raw content on the search call or through extract. Brave, Keenable, Serper and SerpApi return snippets and metadata only, though Brave offers up to 5 extra snippets per result. To run the comparison loop above with one key, start at the [quickstart](https://getmux.io/docs/quickstart). --- # Exa vs Parallel vs Keenable: which search API to pick Exa vs Parallel vs Keenable compared on latency, price per 1,000 searches, result caps, full content and filters, with a decision table for agent builders. Source: https://getmux.io/compare/exa-vs-parallel-vs-keenable Exa, Parallel and Keenable are all web search APIs built for programs rather than people, and they overlap more than they differ. Parallel is the cheapest and fastest at the low end, Exa has the deepest semantic search and the only true full-page content, and Keenable offers a flat price with a realtime index option. Through Mux you can call all three with one key and let the request fall back from one to the next, so the choice is often "in what order" rather than "which one". This page compares the three as they appear through Mux, using the list prices Mux passes through. Each vendor also sells directly, and at very high volume with a negotiated contract a direct relationship can be the better deal. If you only care about the first two, [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa) goes deeper on that pair. ## The short version | | Exa | Parallel | Keenable | |---|---|---|---| | Cheapest tier (per 1k requests, 10 results) | $4.00 (`exa/instant`) | $1.00 (`parallel/turbo`, `parallel/fast`) | $4.00 (`keenable/pro`, `keenable/realtime`) | | Default-quality tier | $7.00 (`exa/auto`, ~1 s) | $5.00 (`parallel/basic`, ~1 s) | $4.00 (`keenable/pro`, ~700 ms) | | Top tier | $12.00 (`exa/deep`, ~5 s) | $5.00 (`parallel/advanced`, ~3 s) | same as above | | Fastest tier latency | ~300 ms | ~200 ms | ~700 ms | | Extra results beyond 10 | $1.00 per 1k results | $1.00 per 1k results | free | | Max results per request | 100 | 50 | 50 | | `content: "full"` | full page text | joined excerpts | not supported | | `published_before` | yes | not applied | yes | | `include_domains` | native | native | one native, rest filtered by Mux | | Provider options via Mux | `category`, `userLocation`, `moderation`, `includeText`, `excludeText` | `max_chars_total`, `client_model`, `advanced_settings.*` | none | Prices are per 1,000 requests. One `exa/auto` search is $0.007, one `parallel/fast` search is $0.001, one `keenable/pro` search is $0.004. See [/docs/pricing](https://getmux.io/docs/pricing) for the billing rules. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Latency tiers Each provider exposes several speeds, and Mux names them `provider/variant`. - **Parallel** spans the widest range: `parallel/turbo` at roughly 200 ms, `parallel/fast` around 800 ms, `parallel/basic` around 1 s and `parallel/advanced` around 3 s for higher-quality excerpts. - **Exa** goes from `exa/instant` at roughly 300 ms to `exa/deep`, a multi-query search that takes about 5 s. `exa/auto` at about 1 s is the general-purpose default. - **Keenable** has two variants, `pro` and `realtime`, both around 700 ms. They differ by index, not speed. These are typical figures, not guarantees. The `usage.latency_ms` field in every Mux response tells you what you actually got. ## Price per 1,000 requests Parallel's two $1.00 tiers are the floor. Nothing else in the table comes within a factor of four. If your agent does many cheap lookups where a decent snippet is enough, [`parallel/fast`](https://getmux.io/models/parallel/fast) or `parallel/turbo` is where to start. The per-tier math for each provider is in [Parallel search API pricing](https://getmux.io/guides/parallel-search-api-pricing), [Exa pricing explained](https://getmux.io/guides/exa-pricing-explained) and the [Keenable search API guide](https://getmux.io/guides/keenable-search-api). Keenable's $4.00 is flat: no extra charge for results past 10, so a 50-result Keenable request still costs $0.004. On Exa and Parallel the same 50 results add $0.04 in extra-result charges, which is more than the base price. Exa is the most expensive per request, and [`exa/deep`](https://getmux.io/models/exa/deep) at $12.00 is the most expensive model Mux offers. You pay for it when the query is hard and a single pass will not do. ## Result caps and full content Exa is the only provider that returns up to 100 results per request. Parallel and Keenable stop at 50. Mux caps `max_results` to the model's limit rather than erroring. `content: "full"` behaves differently on each: - Exa returns the full page text in `results[].content`. - Parallel returns its excerpts joined together, which is longer than a snippet but not the whole page. - Keenable returns `content: null`. If your pipeline needs page text, do not put a Keenable model first. ## Date and domain filters All three accept `published_after` and `include_domains` / `exclude_domains`. Two caveats: Parallel does not apply `published_before`, and Keenable natively supports one domain. Mux enforces domain filters on the response regardless of provider, so results will never violate your filter, but on Keenable a long `include_domains` list may leave you with fewer results than you asked for. ## Provider options Mux passes through an allow-list of provider-specific options. Exa gets `category`, `userLocation`, `moderation`, `includeText` and `excludeText`. Parallel gets `max_chars_total`, `client_model` and `advanced_settings` for excerpts and location. Keenable has none. If a workflow depends on one of these, that decides the provider for you. ## Typical fit | Workload | Start with | Why | |---|---|---| | High-volume, cheap lookups (entity checks, URL discovery) | `parallel/fast` | $1.00 per 1k, ~800 ms | | Lowest latency in a chat loop | `parallel/turbo` or `exa/instant` | ~200 to 300 ms | | Semantic or research queries | `exa/auto`, then `exa/deep` | neural search, 100 results, full text | | Full page text for RAG | `exa/auto` with `content: "full"` | only provider returning whole pages | | Many results per query at fixed cost | `keenable/pro` | no extra-result charge up to 50 | | Very recent pages | `keenable/realtime` | realtime index | | Highest-quality excerpts without full pages | `parallel/advanced` | $5.00, ~3 s | ## Use fallbacks instead of choosing The `models` field takes an ordered list of up to five model ids. Mux tries them in order, refunds any failed attempt, and bills you only for the model that answered. This means you can lead with the cheap option and keep a stronger one behind it. Recommended orders for cost, quality and latency are in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). ```bash curl -X POST https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "postgres logical replication slot lag", "models": ["parallel/fast", "exa/auto", "keenable/pro"], "max_results": 10, "published_after": "2025-01-01" }' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); // reads MUX_API_KEY const res = await mux.search({ query: "postgres logical replication slot lag", models: ["parallel/fast", "exa/auto", "keenable/pro"], maxResults: 10, publishedAfter: "2025-01-01", }); console.log(res.model, res.usage.costUsd, res.results.length); ``` ```python from getmux import Mux mux = Mux() res = mux.search( "postgres logical replication slot lag", models=["parallel/fast", "exa/auto", "keenable/pro"], max_results=10, published_after="2025-01-01", ) print(res.model, res.cost_usd, [r.url for r in res.results]) ``` The response's `attempts` array shows what happened at each step, so you can see how often the fallback fires. The default `"auto"` model is itself a fallback chain: `parallel/fast`, then `exa/auto`, then `keenable/pro`. One exception to the refund rule: if a provider times out at 15 s, you still pay that model's base price before the next one is tried. ## When to go direct Pick a vendor directly if you need something Mux does not expose, such as a crawl or contents endpoint, an answer endpoint, or options outside the allow-list. At sustained high volume with a negotiated contract, a direct relationship may also beat list price. Mux charges no per-request markup, only a 5.5% fee when you buy credits, and with [BYOK](https://getmux.io/docs/byok) you can keep your own vendor keys and still use the fallback chain. ## FAQ ### Which of Exa, Parallel and Keenable is cheapest? Parallel, at $1.00 per 1,000 requests for `parallel/turbo` and `parallel/fast`. Exa's cheapest tier is `exa/instant` at $4.00 and Keenable's two variants are $4.00. Keenable is cheapest when you regularly ask for 30 to 50 results, because it does not charge for extra results. Outside these three, Mux also routes to [`deepseek/search`](https://getmux.io/models/deepseek/search) at $0.75 per 1,000 requests, flat for up to 50 results, with a 128-character query limit. ### Which one returns full page content? Only Exa returns full page text with `content: "full"`. Parallel returns joined excerpts and Keenable returns no content field. If you need whole pages, put an Exa model first in `models`. ### Do date and domain filters work on all three? Mostly. All three honor `published_after` and domain filters. Parallel ignores `published_before`, and Keenable supports one domain natively with Mux filtering the rest after the fact. ### Can I use my own Exa, Parallel or Keenable key through Mux? Yes. Store it with `PUT /v1/provider-keys/{provider}` and requests to that provider are billed to you by the vendor. The Mux fee is waived for the first 1,000,000 BYOK requests per calendar month, then 5% of list price. To try the three side by side with one key, start at the [quickstart](https://getmux.io/docs/quickstart). --- # Exa vs Tavily: which search API for your agent Exa vs Tavily compared for agent builders on price per 1,000 searches, result caps, full page text, filters, extract and crawl, with guidance on when each is the better pick. Source: https://getmux.io/compare/exa-vs-tavily Exa and Tavily are both web search APIs built for programs rather than people, and the practical difference is what you get back and how you pay. Exa is a neural search engine that returns up to 100 results per request with full page text inline, priced per request from $4 to $12 per 1,000. Tavily is a credit-based search, extract and crawl product with a free tier, a 20-result cap, and a pay-as-you-go rate that works out to $8 per 1,000 basic searches. Pick Exa when you want deep semantic retrieval and whole pages in one call; pick Tavily when you want search plus extraction plus crawl from one vendor, or your volume fits its free or monthly plans. This page compares the two as of October 2026 using each vendor's public pricing. Exa is also available through Mux as [`exa/auto`](https://getmux.io/models/exa/auto) and three other tiers at the same list price; Tavily is not, so the Tavily figures are what you would pay Tavily directly. ## The short version | | Exa | Tavily | |---|---|---| | Pricing model | per request, 10 results included | credits, 1 or 2 per search | | Cheapest search | $4 per 1k (`lite` direct, `exa/instant` via Mux) | $8 per 1k basic on pay-as-you-go | | Default search | $7 per 1k (`auto`) | $8 per 1k basic, $16 per 1k advanced | | Deepest search | $12 per 1k (`deep`) | $16 per 1k advanced, or Research at 4 to 250 credits | | Max results per request | 100 | 20 | | Extra results | $1 per 1k results beyond 10 | n/a, cap is 20 | | Full page text | inline, `content: "full"` through Mux | `include_raw_content` on search, or the extract endpoint | | Date filters | `published_after`, `published_before` | `time_range`, `start_date`, `end_date` | | Domain filters | include and exclude | include up to 300, exclude up to 150 | | Extract and crawl | contents endpoint direct; not via Mux | extract, crawl and map endpoints | | Free tier | $10 in credit per month on signup | 1,000 credits per month | Sources: [exa.ai/pricing](https://exa.ai/pricing), [tavily.com/pricing](https://www.tavily.com/pricing), [Tavily API credits](https://docs.tavily.com/documentation/api-credits) and the [Tavily search reference](https://docs.tavily.com/documentation/api-reference/endpoint/search). ## How each one prices a search Exa's price is per request. As of October 2026 its [pricing page](https://exa.ai/pricing) lists `lite` at $4 per 1,000 requests, `auto` at $7, and `deep` at $12, each including 10 results, with $1 per 1,000 for results beyond the tenth. Contents such as page text, highlights and summaries are billed at $1 per 1,000 pages per content type when you call Exa directly. Through Mux the four tiers are [`exa/instant`](https://getmux.io/models/exa/instant) at $4, [`exa/fast`](https://getmux.io/models/exa/fast) and [`exa/auto`](https://getmux.io/models/exa/auto) at $7, and [`exa/deep`](https://getmux.io/models/exa/deep) at $12, with the same extra-result rate, and `content: "full"` does not add a charge. The full math, including what 25 and 100 results cost, is in [Exa pricing explained](https://getmux.io/guides/exa-pricing-explained). Tavily's price is in credits. Its [pricing page](https://www.tavily.com/pricing) lists a free Researcher plan with 1,000 credits per month, pay-as-you-go at $0.008 per credit, a Project plan at $30 per month for 4,000 credits, and custom enterprise terms. A basic, fast or ultra-fast search costs 1 credit and an advanced search costs 2, so pay-as-you-go is $8 per 1,000 basic searches or $16 per 1,000 advanced. Monthly plans bring the per-credit rate down, so a team doing a steady volume should price its plan rather than the pay-as-you-go rate. Two things decide which is cheaper for you. At low volume Tavily's free 1,000 credits per month beat anything Exa offers except its own $10 monthly free credit, which covers up to 2,500 `lite` searches. At higher volume Exa's `auto` at $7 per 1,000 undercuts Tavily's pay-as-you-go basic rate, and `lite` or `exa/instant` at $4 is about half of it. Whether Tavily's monthly plans close that gap depends on your tier, so check the page. ## Result count and page text Exa returns up to 100 results per request. Tavily's `max_results` tops out at 20. For agents that cast a wide net and rank locally, or for building a candidate list of sources, that is a real difference. Note that on Exa the extra results add up: 100 results on `exa/auto` cost $0.097 rather than $0.007, so wide queries are not free on either side. Both can hand you page text. Exa does it inline. Through Mux, `content: "full"` on any Exa model fills `results[].content` with the full page at no extra charge; direct from Exa, text is a contents item billed per page. Tavily has `include_raw_content` on the search call, which returns page content as markdown or text and may add latency, and a separate extract endpoint billed at 1 credit per 5 successful URLs for basic extraction. If you often want text for a handful of results rather than all of them, Tavily's extract is the cheaper shape. If you want text for every result on every search, Exa's inline model is simpler. ## Filters and query style Tavily exposes more knobs. Its search takes `topic` (general, news or finance), `time_range` shorthands like `week` and `month`, explicit `start_date` and `end_date`, up to 300 include domains and 150 exclude domains, a country bias, and an `include_answer` flag that returns a short LLM-written answer alongside results. Exa's filters cover published dates, include and exclude domains, a `category` such as research papers or news, and text-must-include and text-must-exclude constraints, plus `userLocation` and `moderation`. Through Mux the Exa options arrive as `published_after`, `published_before`, `include_domains`, `exclude_domains` and a small set of `provider_options`. Exa's index is neural, which matters for the kind of query an agent writes. A long natural-language question such as "startups building solid-state battery electrolytes that raised in 2026" works well on Exa and is the use case it was designed for. Tavily handles natural-language queries too, but the distinctive features on its side are the vertical topics and the one-call answer, not semantic retrieval over a very large result set. ## Extract, crawl and research This is where Tavily is the broader product. Alongside search it offers extract (page text for a list of URLs), map (discover the pages of a site), crawl (map plus extract) and a research endpoint that runs a multi-step investigation for 4 to 250 credits per request depending on the model. An agent that needs to walk a documentation site or pull clean text from a known list of URLs can do that from one vendor with one bill. Exa sells a contents endpoint and other products directly, but through Mux you get search only. If crawl is part of the job, that pushes you toward Tavily, or toward Exa direct, rather than a router. The same caveat applies to every Mux article, including the broader [Tavily alternatives](https://getmux.io/compare/tavily-alternatives) roundup. ## Latency Neither vendor publishes measured latency on its pricing page. Tavily describes `ultra-fast` as minimizing latency above all else, `fast` as prioritizing lower latency, and `advanced` as highest relevance with increased latency, and returns a `response_time` field on each call. Through Mux the typical figures seen for Exa are about 300 ms for `exa/instant`, 500 ms for `exa/fast`, 1 s for `exa/auto` and 5 s for `exa/deep`, and every response carries `usage.latency_ms`. Measure both on your own queries before you commit; the shape of the request, especially page text, moves latency more than the vendor does. ## When Tavily is the better choice - You need extract, map or crawl as well as search and want one vendor for all of it. - Your volume fits inside 1,000 free credits a month, or a monthly plan that prices below $7 per 1,000 for your tier. - You want vertical topics (news, finance), a 300-domain allow-list, or a one-call `include_answer` summary. - You already have Tavily wired into your framework and the switching cost is real. ## When Exa is the better choice - You want semantic search over a large neural index, with natural-language queries as the norm. - You need more than 20 results per request, up to 100. - You want full page text inline for every result in a single call. - You want to run Exa alongside other providers with fallback. Through Mux you can put [`exa/auto`](https://getmux.io/models/exa/auto) behind [`parallel/fast`](https://getmux.io/models/parallel/fast) in one `models` list and pay only for the model that answered; see [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). ## Trying Exa with one request Through Mux, an Exa search with full page text looks like this. Swap `model` for any other Mux model id and the response shape does not change. ```bash curl -X POST https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "solid-state battery electrolyte startups 2026", "model": "exa/auto", "max_results": 10, "content": "full"}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "solid-state battery electrolyte startups 2026", model: "exa/auto", maxResults: 10, content: "full", }); console.log(res.usage.costUsd, res.results[0]?.content?.length); ``` ```python from getmux import Mux mux = Mux() res = mux.search("solid-state battery electrolyte startups 2026", model="exa/auto", max_results=10, content="full") print(res.cost_usd, len(res.results[0].content or "")) ``` | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## FAQ ### Is Exa or Tavily cheaper per search? At list prices as of October 2026, Exa's `auto` at $7 per 1,000 requests and `lite` at $4 are below Tavily's pay-as-you-go rate of $8 per 1,000 basic searches. Tavily's free tier of 1,000 credits a month and its monthly plans change that at low volume, so compare against the plan you would actually buy on [tavily.com/pricing](https://www.tavily.com/pricing). ### Which returns full page content? Both. Exa returns it inline, and through Mux `content: "full"` on an Exa model costs nothing extra. Tavily returns it with `include_raw_content` on the search call or through its extract endpoint at 1 credit per 5 URLs for basic extraction. ### Can I use Tavily through Mux? No. Mux routes to Exa, Parallel, Keenable and DeepSeek. If Tavily is the right fit, go to Tavily directly. The [buyer's guide to web search APIs for AI agents](https://getmux.io/compare/web-search-apis-for-ai-agents) covers how to evaluate the wider field. ### Does Exa or Tavily have a free tier? Both. Exa gives new signups $10 in credit that resets monthly, enough for up to 2,500 `lite` searches. Tavily's Researcher plan includes 1,000 credits per month with no card. Mux has no free tier; credits are prepaid from $5. To run an Exa search next to Parallel and Keenable with one key, start at the [quickstart](https://getmux.io/docs/quickstart). --- # Parallel vs Exa: price, latency and content compared Parallel vs Exa for agent builders, comparing the four tiers of each on price per 1,000 searches, latency, result caps, excerpts vs full page text, filters and when to use both. Source: https://getmux.io/compare/parallel-vs-exa Parallel and Exa are the two search APIs most often shortlisted by teams building agents, and they split cleanly on price and depth. Parallel's `turbo` and `fast` tiers cost $1 per 1,000 requests and return LLM-ready excerpts up to 50 results, with higher-quality `basic` and `advanced` tiers at $5. Exa runs from $4 to $12 per 1,000, returns up to 100 results, and is the only one of the two that hands back whole pages inline. Start with Parallel for high-volume, low-cost lookups and move to Exa when a query needs semantic depth, a large result set or full page text. Through Mux both are available on one key with the same request shape, so the usual answer is an ordered list rather than a single pick. Prices below are the vendors' list prices as of October 2026, from [parallel.ai/pricing](https://parallel.ai/pricing) and [exa.ai/pricing](https://exa.ai/pricing), which are the same prices Mux passes through with no per-request markup. ## The short version | | Parallel | Exa | |---|---|---| | Cheapest tier | $1 per 1k ([`parallel/turbo`](https://getmux.io/models/parallel/turbo), [`parallel/fast`](https://getmux.io/models/parallel/fast)) | $4 per 1k ([`exa/instant`](https://getmux.io/models/exa/instant)) | | Default-quality tier | $5 per 1k ([`parallel/basic`](https://getmux.io/models/parallel/basic)) | $7 per 1k ([`exa/auto`](https://getmux.io/models/exa/auto)) | | Top tier | $5 per 1k ([`parallel/advanced`](https://getmux.io/models/parallel/advanced)) | $12 per 1k ([`exa/deep`](https://getmux.io/models/exa/deep)) | | Extra results beyond 10 | $1 per 1k results | $1 per 1k results | | Max results per request | 50 | 100 | | Typical fastest latency via Mux | ~200 ms (`turbo`) | ~300 ms (`instant`) | | Typical top-tier latency via Mux | ~3 s (`advanced`) | ~5 s (`deep`) | | `content: "full"` via Mux | joined excerpts | full page text | | `published_before` | not applied | applied | | Free credit direct | up to $80 on signup plus $5 a month | $10 a month | | Direct rate limit | 600 requests per minute | not published on pricing page | | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Price The price gap at the bottom is four to one. A 10-result `parallel/fast` search is $0.001; the cheapest Exa tier is $0.004. At the top the gap is less than it looks: `parallel/advanced` at $0.005 against `exa/auto` at $0.007 is 40 percent, and `exa/deep` at $0.012 is a different kind of product, running several queries upstream for one request. Both charge $1 per 1,000 for results past the tenth, billed on what the provider returns and capped at what you asked for. That extra-result charge dominates once you ask for many results. A 50-result search costs $0.041 on `parallel/fast` and $0.047 on `exa/auto`, so at wide result counts the tier you picked matters less than the count. The per-tier tables are in [Parallel search API pricing](https://getmux.io/guides/parallel-search-api-pricing) and [Exa pricing explained](https://getmux.io/guides/exa-pricing-explained). Free credit is where Parallel is unusually generous for evaluation: its pricing page lists up to $80 on signup plus $5 every month, which it describes as up to 5,000 requests a month free. Exa gives $10 a month, enough for up to 2,500 of its `lite` searches. Neither of those applies through Mux, which bills prepaid credits at list price. ## Latency Each vendor sells a spread of speeds. Through Mux the typical figures are: - **Parallel**: `turbo` about 200 ms, `fast` about 800 ms, `basic` about 1 s, `advanced` about 3 s. - **Exa**: `instant` about 300 ms, `fast` about 500 ms, `auto` about 1 s, `deep` about 5 s. For an interactive agent, `parallel/turbo` and `exa/instant` are the two sub-half-second options, and the difference between them is small enough that quality on your queries should decide. For a background research job the slow tiers are fine and the question becomes depth, which is where Exa pulls ahead. These numbers are typical rather than guaranteed; `usage.latency_ms` on each Mux response is the real figure. ## What comes back This is the clearest difference. Parallel returns excerpts selected for relevance to the query, which is what an LLM context window wants. Set `content: "full"` through Mux and you get those excerpts joined together, longer than a snippet but never a whole page. Parallel also accepts a `client_model` hint and a `max_chars_total` budget so you can size the excerpts to the model that will read them. Exa returns search results and, with `content: "full"`, the complete page text in `results[].content`, with no extra charge through Mux. For RAG, summarization or anything that needs to quote from a page, this saves a second fetch. For a tool call inside a chat loop it is often too much, and you will want to trim or request snippets only. Exa also goes to 100 results per request where Parallel stops at 50. Mux caps `max_results` to the model's limit rather than erroring, so a request for 80 on a Parallel model returns at most 50 and is billed for at most 50. ## Filters Both apply `published_after` and include and exclude domain lists natively. Parallel does not apply `published_before`, so a request with an upper date bound can return newer pages and is billed for them; if you need both bounds, route to Exa. Domain filters are also re-checked by Mux on the response regardless of provider, so results never violate your list. Exa accepts a `category` (research papers, news, companies and so on), `includeText` and `excludeText` phrase constraints, `userLocation` and `moderation` through `provider_options`. Parallel accepts `max_chars_total`, `client_model` and `advanced_settings` for excerpt tuning and location. If a workflow depends on one of these, that chooses the provider. ## Index and query style Exa's index is neural and built for semantic queries. Long, descriptive, natural-language questions do well, and the `deep` tier decomposes a hard question into several searches. Parallel positions its search around returning excerpts an agent can act on at low cost and latency, with the `advanced` processor for higher-quality excerpts. In practice, most teams find `parallel/fast` good enough for entity lookups, URL discovery and fact checks, and reach for `exa/auto` or `exa/deep` when the question is open-ended or the agent needs to read rather than skim. ## Fit by workload | Workload | Start with | Why | |---|---|---| | High-volume cheap lookups | `parallel/fast` | $0.001 per search, ~800 ms | | Lowest latency in a chat loop | `parallel/turbo` | ~200 ms at $0.001 | | Research-style or semantic queries | `exa/auto` | neural index, 100 results | | Full page text for RAG | `exa/auto` with `content: "full"` | whole pages inline, no extra charge via Mux | | Best excerpts without whole pages | `parallel/advanced` | $0.005, tuned for LLM consumption | | Hard multi-hop questions | `exa/deep` | multi-query search at $0.012 | | Upper date bound required | any Exa model | Parallel ignores `published_before` | ## Use both Because the two differ in price and depth rather than overlapping, they pair well. The default Mux `auto` chain is `parallel/fast`, then `exa/auto`, then `keenable/pro`: cheap first, deeper second, a third independent index last. A failed attempt is refunded and you pay for the model that answered. An agent that wants breadth and then depth can also split the work explicitly, discovering with Parallel and reading with Exa; the worked cost of that pattern is in [web search cost per agent run](https://getmux.io/guides/web-search-cost-per-agent-run). ```bash curl -X POST https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "wasm component model adoption 2026", "models": ["parallel/fast", "exa/auto"], "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "wasm component model adoption 2026", models: ["parallel/fast", "exa/auto"], maxResults: 10, }); console.log(res.model, res.usage.costUsd, res.attempts.length); ``` ```python from getmux import Mux mux = Mux() res = mux.search("wasm component model adoption 2026", models=["parallel/fast", "exa/auto"], max_results=10) print(res.model, res.cost_usd, len(res.attempts)) ``` The order matters for content. If a request needs page text, put the Exa model first, because a successful Parallel answer ends the chain with excerpts rather than pages. How to read the `attempts` array and build each order is covered in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents). ## When to go direct Parallel's free credit and 600 requests per minute direct limit make it the easier one to evaluate without an intermediary, and Parallel also sells an extract API and a task API that Mux does not proxy. Exa sells a contents endpoint and other products beyond search. If you need any of those, or you have a negotiated contract at volume, go to the vendor. With [BYOK](https://getmux.io/docs/byok) you can store either key in Mux and keep the fallback chain while the vendor bills you. ## FAQ ### Is Parallel or Exa cheaper? Parallel. Its `turbo` and `fast` tiers are $1 per 1,000 requests against Exa's cheapest at $4, and its top tier at $5 is below Exa's default `auto` at $7. Both add $1 per 1,000 results beyond 10, which narrows the gap on wide result sets. ### Which one returns full page text? Only Exa. Through Mux, `content: "full"` on an Exa model returns the whole page in `results[].content`. On Parallel the same flag returns the provider's excerpts joined together. ### Which is faster? Their fastest tiers are close: `parallel/turbo` is typically about 200 ms and `exa/instant` about 300 ms through Mux. At the top end `parallel/advanced` is around 3 s and `exa/deep` around 5 s, because `deep` runs multiple queries for one request. ### Can I run both from one integration? Yes. Mux exposes both behind one endpoint with the same request and response shape, and the `models` field takes an ordered fallback list. The third provider in the default chain, Keenable, is compared alongside these two in [Exa vs Parallel vs Keenable](https://getmux.io/compare/exa-vs-parallel-vs-keenable). To try both with one key, start at the [quickstart](https://getmux.io/docs/quickstart). --- # SerpApi alternatives: Google SERP vs agent search APIs SerpApi alternatives compared for agent builders, covering Serper and Brave for SERP-style results and Exa, Parallel, Keenable and DeepSeek for LLM-oriented search, with prices per 1,000. Source: https://getmux.io/compare/serpapi-alternatives SerpApi alternatives fall into two groups. If you need Google's results as JSON, including knowledge panels, People Also Ask and verticals like shopping or scholar, the close substitutes are Serper and, for an independent index, Brave Search API. If you are feeding results to a language model and do not care whether they match what Google shows, the agent-oriented search APIs, Exa, Parallel, Keenable and DeepSeek, cost between $0.75 and $12 per 1,000 requests at list price against SerpApi's $5.50 to $25 per 1,000 depending on plan, and return results shaped for a prompt rather than a results page. Mux routes to those four behind one key, which is a quick way to test whether your agent actually needs a SERP. SerpApi is a mature product and the right tool for many jobs. The point of this page is to help you decide which group you are in before you switch. ## What SerpApi does SerpApi scrapes search engine results pages and returns them as structured JSON. Beyond Google it covers Bing, DuckDuckGo, Yahoo, Baidu, Yandex and Naver, plus Google verticals including news, images, shopping, scholar, maps, jobs, patents, trends and AI Overview. The output mirrors the page: organic results, ads, knowledge graph, related questions, local pack and so on, which is exactly what SEO tooling, price monitoring and brand tracking want. As of October 2026 its [pricing page](https://serpapi.com/pricing) lists monthly plans: | Plan | Price per month | Searches per month | Effective per 1,000 | |---|---|---|---| | Free | $0 | 250 | n/a | | Starter | $25 | 1,000 | $25.00 | | Developer | $75 | 5,000 | $15.00 | | Production | $150 | 15,000 | $10.00 | | Big Data | $275 | 30,000 | $9.17 | | Searcher | $725 | 100,000 | $7.25 | | Volume | $1,475 | 250,000 | $5.90 | | Infrastructure | $2,750 | 500,000 | $5.50 | | Cloud | from $3,750 | from 1,000,000 | from $3.75 | Only successful searches count, and a response with 100 results or an empty result set each count as one. Faster "Ludicrous Speed" modes cost 2x or 4x the base rate. Plans carry an hourly throughput ceiling, from 50 searches an hour on Free to 100,000 on Infrastructure. Production and above include a U.S. Legal Shield covering scraping and parsing of search data, and Cloud plans add a ZeroTrace mode that stops retention of queries and results. SerpApi is the better choice when: - You need the SERP itself: rankings, ads, featured snippets, knowledge graph, local pack. - You need a vertical no agent search API covers, such as shopping, jobs, flights or patents. - You need results from Bing, Yahoo, Baidu, Yandex or Naver specifically. - The Legal Shield matters to your compliance team. ## SERP-style alternatives ### Serper Serper returns Google results as JSON across search, images, news, maps, places, videos, shopping, scholar, patents and autocomplete, and its [site](https://serper.dev) advertises results in 1 to 2 seconds with 2,500 free queries and no card. Its pricing is prepaid credits rather than a subscription, and third-party write-ups in 2026 describe packs starting at $50 for 50,000 credits, which would be $1 per 1,000, with credits expiring after six months and a query for more than 10 results costing two credits. We could not load Serper's public pricing page when checking in October 2026, so confirm the pack prices inside your account before relying on them. If those figures hold, Serper is the cheaper SERP source by a wide margin, and it is usually the first thing teams try when SerpApi's plan math stops working. SerpApi's advantages are the breadth of engines, the Legal Shield and plan-level throughput guarantees. ### Brave Search API Brave is not a Google scraper; it runs an independent index and sells access at $5 per 1,000 requests with $5 in monthly free credit and a 50 queries per second limit, per its [API page](https://brave.com/search/api/) as of October 2026. Results are 20 per page with up to 9 further pages, with news, images and video verticals, freshness filters and up to 5 extra snippets per result. It is the alternative when you want SERP-like results and verticals but would rather not depend on Google's output. The [Brave Search API alternatives](https://getmux.io/compare/brave-search-api-alternatives) page covers Brave in more detail and what to use instead of it. ## Agent-oriented alternatives If your consumer is a model rather than a dashboard, the result shape SerpApi returns is mostly overhead. The APIs below return a ranked list with title, URL, snippet or excerpt, published date and optionally page text, and price per request rather than per plan. ### Parallel Parallel's [`parallel/fast`](https://getmux.io/models/parallel/fast) and [`parallel/turbo`](https://getmux.io/models/parallel/turbo) are $1 per 1,000 requests through Mux, with `turbo` typically answering in about 200 ms, and the higher-quality `basic` and `advanced` tiers are $5. Results are excerpts chosen for an LLM, up to 50 per request, with $1 per 1,000 for results beyond 10. At $0.001 a search it is 25 times cheaper than SerpApi's Starter plan and a bit below its largest Cloud tier. Direct access and free credit are at [parallel.ai/pricing](https://parallel.ai/pricing). ### Exa Exa is a neural search engine built for semantic queries. Through Mux, [`exa/instant`](https://getmux.io/models/exa/instant) is $4 per 1,000, [`exa/auto`](https://getmux.io/models/exa/auto) $7 and `exa/deep` $12, with up to 100 results and full page text inline with `content: "full"` at no extra charge. Exa is the one to pick when the agent's queries are long natural-language questions, or when it needs to read pages rather than skim snippets. How the tiers compare with Parallel's is in [Parallel vs Exa](https://getmux.io/compare/parallel-vs-exa). ### Keenable Keenable runs its own index and is a flat $4 per 1,000 requests through Mux as [`keenable/pro`](https://getmux.io/models/keenable/pro) or `keenable/realtime`, with no charge for results beyond 10 up to 50. It returns snippets only, no page text, which makes it the closest in output shape to a plain organic-results list. The realtime variant is built for recency. ### DeepSeek `deepseek/search` is $0.75 per 1,000 requests through Mux, flat for up to 50 results, with no charge when a search returns nothing. Each result has a long model-oriented summary and `content: "full"` returns page text. Queries are limited to 128 characters including operators, so it suits short keyword lookups, which happen to be the kind of query most SerpApi integrations already send. ### Tavily Tavily sits between the groups: an agent-oriented search API with extract, map and crawl endpoints, a 20-result cap, 1,000 free credits a month and pay-as-you-go at $0.008 per credit, with a basic search at 1 credit and an advanced one at 2 per its [pricing page](https://www.tavily.com/pricing) and [credits reference](https://docs.tavily.com/documentation/api-credits). It is covered against the rest of the field in [Tavily alternatives](https://getmux.io/compare/tavily-alternatives). ## Comparison at a glance | Provider | Returns | Price per 1k (list) | Max results | Page text | Via Mux | |---|---|---|---|---|---| | SerpApi | Google and other SERPs as JSON | $5.50 to $25 by plan | per engine | no | no | | Serper | Google SERP as JSON | see account | 10 per credit | no | no | | Brave Search | own-index results and verticals | $5 | 20 per page | no | no | | Parallel | excerpts for LLMs | $1 to $5 | 50 | excerpts | yes | | Exa | neural search results | $4 to $12 | 100 | full, inline | yes | | Keenable | own-index snippets | $4 flat | 50 | no | yes | | DeepSeek | results with summaries | $0.75 flat | 50 | full, inline | yes | | Tavily | results, optional answer | $8 pay-as-you-go | 20 | raw content or extract | no | Prices are as of October 2026. Mux passes through list prices with no per-request markup; its only fee is 5.5% (minimum $0.80) when you buy credits. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Deciding which group you are in Ask what your code does with the response. If it reads `organic_results[0].position`, parses the knowledge graph, or compares rankings over time, you need a SERP API and your choice is SerpApi, Serper or, for an independent index, Brave. If it concatenates titles and snippets into a prompt and lets a model decide, you need retrieval, and the agent-oriented APIs are cheaper, return more useful text, and let you filter by domain and date without query operators. A second test is the query. SerpApi integrations tend to send short keyword strings because that is what Google expects. Agents increasingly send full sentences. Parallel and Exa handle both; DeepSeek wants the short form; SERP APIs quietly degrade on the long form. ## Trying the agent-oriented group with one key Through Mux, the four agent-oriented providers share a request and response shape, so a migration from SerpApi is one adapter rather than four, and the provider becomes a string. ```bash curl -X POST https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "best budget mechanical keyboard 2026", "models": ["parallel/fast", "exa/auto"], "max_results": 10}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); const res = await mux.search({ query: "best budget mechanical keyboard 2026", models: ["parallel/fast", "exa/auto"], maxResults: 10, }); for (const r of res.results) console.log(r.title, r.url, r.publishedDate); ``` ```python from getmux import Mux mux = Mux() res = mux.search("best budget mechanical keyboard 2026", models=["parallel/fast", "exa/auto"], max_results=10) for r in res.results: print(r.title, r.url, r.published_date) ``` SerpApi plans are monthly with an hourly throughput ceiling; Mux is prepaid with no subscription, 20 requests per second per key and a per-key USD limit you can set per agent. Budgeting a migration is covered in [web search cost per agent run](https://getmux.io/guides/web-search-cost-per-agent-run). ## When Mux is the wrong choice Mux does not route to SerpApi, Serper or Brave, and it has no SERP features, no shopping or scholar verticals, and no crawl or extract. If your product depends on any of those, stay on a SERP API. At sustained high volume on one provider a negotiated contract may beat list price, and [BYOK](https://getmux.io/docs/byok) lets you keep your own vendor key while using the fallback chain. ## FAQ ### What is the cheapest SerpApi alternative? For Google results as JSON, Serper is widely reported to be cheaper per query, but its public pricing was not available when we checked in October 2026, so verify in your account. For agent retrieval that does not need to match Google, `deepseek/search` at $0.75 and `parallel/fast` at $1 per 1,000 requests through Mux are the lowest list prices. ### Do I need Google's results or just search results? If your code parses SERP features such as rankings, ads or the knowledge graph, you need a SERP API. If it hands titles, snippets or page text to a model, an agent-oriented search API is cheaper and returns more usable text per result. ### Does SerpApi charge for failed or empty searches? Per its pricing page, only successful searches count toward a plan, and cached or errored searches do not. An empty result set still counts as one search. For comparison, Mux refunds failed provider attempts and DeepSeek does not bill searches that return nothing. ### Can I get page text from any SerpApi alternative? Exa and DeepSeek return full page text inline through Mux. Parallel returns excerpts. Tavily returns raw content on the search call or through extract. SerpApi, Serper, Brave and Keenable return snippets and metadata only. To try the agent-oriented providers with one key, start at the [quickstart](https://getmux.io/docs/quickstart). --- # Tavily alternatives: 7 web search APIs for AI agents A neutral look at Tavily alternatives for AI agents, covering Exa, Parallel, Keenable, Brave, Serper, You.com and Sonar, plus when to stay on Tavily. Source: https://getmux.io/compare/tavily-alternatives The main Tavily alternatives for agent builders are Exa, Parallel, Keenable and DeepSeek (search APIs designed for LLM consumption), Brave Search API and Serper (independent or Google-backed SERP results), You.com, and Perplexity Sonar (search bundled with an answer model). Which one fits depends on whether you want raw results to feed your own model, full page text, or a finished answer. Mux lets you try Exa, Parallel, Keenable and DeepSeek with a single key and fall back between them, which is a practical way to evaluate four of the eight at once. Tavily is a solid product and many teams should stay on it. This page is for the cases where price, latency, index freshness or a specific feature pushes you to look around. ## What Tavily does well Tavily positions itself as a search engine built for AI agents, with search, extract and crawl endpoints and integrations in the common agent frameworks. As of October 2026 its [pricing page](https://www.tavily.com/pricing) lists a free tier of 1,000 API credits per month and a pay-as-you-go rate of $0.008 per credit, with a [basic search costing 1 credit and an advanced search 2 credits](https://docs.tavily.com/documentation/api-credits). That works out to $8 per 1,000 basic searches or $16 per 1,000 advanced searches on pay-as-you-go. Monthly plans are priced differently, so check the page for your volume. Staying on Tavily is sensible when: - You rely on its extract or crawl endpoints as well as search. Mux offers neither. - Your volume fits inside the free tier or a monthly plan that works out cheaper for you. - You have framework integrations already wired to it and the switching cost outweighs any saving. ## The alternatives ### Exa Exa is a neural search engine with an index built for semantic queries, which makes it strong on research-style questions where keyword search struggles. Through Mux, [`exa/auto`](https://getmux.io/models/exa/auto) costs $7.00 per 1,000 requests and `exa/instant` $4.00, with results up to 100 per request and full page text available with `content: "full"`. Exa also sells directly at [exa.ai](https://exa.ai), including endpoints Mux does not proxy. For a direct head to head on price, result caps and page text, see [Exa vs Tavily](https://getmux.io/compare/exa-vs-tavily). ### Parallel Parallel's search API is built for agents and prices aggressively at the low end: `parallel/turbo` and [`parallel/fast`](https://getmux.io/models/parallel/fast) are $1.00 per 1,000 requests through Mux, and the higher-quality `parallel/basic` and `parallel/advanced` are $5.00. Results max out at 50 and `content: "full"` returns joined excerpts rather than whole pages. Direct access is at [parallel.ai](https://parallel.ai). ### Keenable Keenable runs its own index and offers a `pro` and a `realtime` variant. Through Mux both are $4.00 per 1,000 requests with no charge for extra results up to 50, which makes it the cheapest option when you want many results per query. It does not return page content. See [keenable.ai](https://keenable.ai) for direct access. ### DeepSeek DeepSeek's search API is the cheapest option on this list: `deepseek/search` is $0.75 per 1,000 requests through Mux, flat for 1 to 50 results, and a search that returns nothing is not billed. Each result carries a long summary written for a model rather than a display snippet, and `content: "full"` returns page text. The constraint is a 128-character query limit, including any domain or date operators. See [DeepSeek's docs](https://api-docs.deepseek.com/guides/search/) for direct access. ### Brave Search API Brave operates an independent web index and sells API access to it. As of October 2026 its [API page](https://brave.com/search/api/) lists a Search plan at $5 per 1,000 requests with $5 in free credits per month and a 50 queries per second rate limit, plus an Answers plan with grounding and citations. Brave is a good fit if you want results from an index that is not Google's or Bing's and you are comfortable integrating one vendor directly. If Brave is the one you are moving away from, see [Brave Search API alternatives](https://getmux.io/compare/brave-search-api-alternatives). ### Serper Serper returns Google search results as JSON, including organic results, knowledge graph, People Also Ask and vertical searches like news, images and scholar. Its [site](https://serper.dev) advertises 2,500 free queries on signup. If your agent needs to see what Google shows, this is the direct route. For current per-query rates see their pricing page, and for how SERP APIs compare with agent-oriented search see [SerpApi alternatives](https://getmux.io/compare/serpapi-alternatives). ### You.com You.com offers web search APIs aimed at AI applications and gives new organizations starter credit to evaluate. It is a reasonable option if you want an LLM-oriented index from a vendor that also runs a consumer product. See [you.com](https://you.com) for current pricing. ### Perplexity Sonar Sonar is different in kind: it is an answer model with built-in search, billed per request plus per token. As of October 2026 Perplexity's [pricing page](https://docs.perplexity.ai/getting-started/pricing) lists `sonar` at $5 to $12 per 1,000 requests depending on search context size, plus $1 per million input and output tokens. Pick Sonar when you want a finished, cited answer rather than results to feed your own model. ## Comparison at a glance | Provider | Returns | Full page text | Pricing model | Via Mux | |---|---|---|---|---| | Tavily | ranked results, optional answer | via extract endpoint | credits, 1 or 2 per search | no | | Exa | neural search results | yes | per request, extra results | yes | | Parallel | results with excerpts | excerpts only | per request, extra results | yes | | Keenable | results from own index | no | flat per request | yes | | DeepSeek | results with model-oriented summaries | yes | flat per request, empty answers free | yes | | Brave | results from own index | no | per request | no | | Serper | Google SERP as JSON | no | credit packs | no | | You.com | LLM-oriented results | no | credits | no | | Perplexity Sonar | answer with citations | n/a | per request plus tokens | no | Mux prices are list prices passed through with no per-request markup. Mux's only fee is 5.5% (minimum $0.80) when you buy credits. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | ## Try four providers with one key A common complaint when evaluating search APIs is that you have to sign up three times and write three adapters before you can compare anything. Mux normalizes Exa, Parallel and Keenable behind one endpoint, so switching the provider is a one-string change and the response shape stays the same. ```bash curl -X POST https://api.getmux.io/v1/search \ -H "Authorization: Bearer $MUX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "rust async runtime comparison 2026", "model": "exa/auto", "max_results": 5}' ``` ```ts import { Mux } from "getmux"; const mux = new Mux(); for (const model of ["parallel/fast", "exa/auto", "keenable/pro"]) { const res = await mux.search({ query: "rust async runtime comparison 2026", model, maxResults: 5 }); console.log(model, res.usage.latencyMs, res.usage.costUsd, res.results[0]?.url); } ``` ```python from getmux import Mux mux = Mux() for model in ["parallel/fast", "exa/auto", "keenable/pro"]: res = mux.search("rust async runtime comparison 2026", model=model, max_results=5) print(model, res.cost_usd, res.results[0].url) ``` Once you know which provider you prefer, keep the others as fallbacks. The `models` field takes an ordered list, failed attempts are refunded, and you pay only for the model that answered (details in [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents)): ```json { "query": "rust async runtime comparison 2026", "models": ["parallel/fast", "exa/auto", "keenable/pro"], "max_results": 10 } ``` Each API key can carry a USD limit that resets daily, weekly or monthly, which is useful when you give each agent or each engineer their own key during an evaluation. See [/docs/keys](https://getmux.io/docs/keys). ## When Mux is the wrong choice Mux is a router over four providers. If the provider you want is Brave, Serper, You.com or Perplexity, go to them directly. If you depend on Tavily's extract or crawl, or any vendor's crawl or contents endpoint, Mux does not replace that. And at very high sustained volume on a single provider, a negotiated direct contract may beat list price. ## FAQ ### What is the cheapest Tavily alternative for high-volume agent search? Among the providers covered here, Parallel's `parallel/fast` and `parallel/turbo` at $1.00 per 1,000 requests through Mux are the lowest list price. Compare that against Tavily's pay-as-you-go rate of $0.008 per credit as of October 2026, which is $8 per 1,000 basic searches. Monthly plans and free tiers change the math at low volume. ### Which alternatives return full page content? Exa returns full page text with `content: "full"`. Parallel returns longer excerpts. Tavily offers page text through a separate extract endpoint. Keenable, Brave and Serper return results and snippets without full pages. ### Can I switch from Tavily to Mux without rewriting my agent? The request shape is different, so you will change the call site, but it is one endpoint with `query`, `max_results`, domain and date filters, and results as `{title, url, snippet, content, published_date}`. The TypeScript and Python SDKs are a few lines each. You keep the ability to change providers afterwards without touching code again. ### Is Mux affiliated with Tavily, Exa, Parallel, Keenable or DeepSeek? No. Mux is an independent service launched October 13, 2026. Exa, Parallel, Keenable and DeepSeek are trademarks of their owners, and Mux passes through their list prices. To run your first search across all four providers, start at the [quickstart](https://getmux.io/docs/quickstart). --- # Web search API for AI agents: a buyer's guide What to look for in a web search API for AI agents, from latency and cost per call to filters and failure handling, with a worked cost example per 1,000 runs. Source: https://getmux.io/compare/web-search-apis-for-ai-agents A web search API for AI agents has to do a different job than one built for a search box: the caller is a program that will issue thousands of queries, read snippets or full pages rather than click, and keep going when a call fails. The things that matter are latency, cost per call, structured results, filters, rate limits and what happens on failure. This guide lists those requirements, compares six providers on qualitative axes, and works through what 1,000 agent runs cost at list price. Mux appears here as a router over Exa, Parallel and Keenable, not as a search engine. The other providers are described from their public positioning, and the numbers quoted for them were checked on their own sites in October 2026. ## What agents need from a search API ### Latency An agent loop that searches three times before answering spends three round trips on search alone. Sub-second tiers exist on every major provider now, and the difference between 200 ms and 3 s is the difference between a usable chat tool and a background job. Decide which steps need the fast tier and which can wait for a better one. ### Cost per call An agent fleet can search a few times a second. At that volume the gap between $1 and $8 per 1,000 calls is the gap between a rounding error and a line item. Also check how extra results are billed, because asking for 50 results can cost more than the base request. ### Structured results You want a stable JSON shape with title, URL, snippet, published date and optionally content, so results go straight into a prompt or a parser. Providers that return Google SERP features serve a different kind of agent than providers that return clean semantic matches. ### Snippets vs full text Snippets are enough for ranking and citation. RAG and summarization usually want full page text. Some providers return it inline, some sell it through a separate extract endpoint, and some do not offer it. This decides whether you need one API call or two per result. ### Date and domain filters Agents often need "only from these sites" or "only after this date". Check which filters the provider applies natively and whether anything is enforced after the fact. Through Mux, domain filters are always enforced on the response as well, and `published_before` is not applied by Parallel models. ### Rate limits A per-second ceiling that fits a single agent can collapse under a batch job. Look for the per-key and per-account numbers, what status code you get, and whether limited requests are billed. Mux allows 20 requests per second per key with bursts to 40, and 50 searches in flight per account. Limited requests return `429` with `Retry-After` and are never charged. ### Failure handling Providers have outages and slow spells. The question is whether your agent degrades or stops. Retries inside an SDK help with transient errors. Falling back to a different provider helps with the rest. If you integrate one vendor directly, plan a second integration or accept the gap. See [search fallbacks for AI agents](https://getmux.io/guides/search-fallbacks-for-ai-agents) for how an ordered provider list behaves. ### Per-agent budgets When many agents share one bill, you need a way to cap each one. Mux API keys carry an optional USD `limit` that resets daily, weekly or monthly, enforced atomically with the charge. A key over its limit gets `402 key_limit_exceeded` and nothing is billed. Create one key per agent or per customer and set the limit when you create it. See [/docs/keys](https://getmux.io/docs/keys) and the guide to [spend limits per API key](https://getmux.io/guides/per-agent-spend-limits). ## Comparison on qualitative axes | | Exa | Parallel | Keenable | DeepSeek | Tavily | Brave Search API | Serper | |---|---|---|---|---|---|---|---| | Index | own neural index | own | own, pro and realtime | own | own | own independent index | Google results | | Built for | semantic and research queries | agent search at low cost | flat-price search | lowest-cost search with a summary per result | agent frameworks, search plus extract and crawl | general search, grounding | Google SERP as JSON | | Full page text | inline with `content: "full"` | excerpts | no | inline with `content: "full"` | separate extract endpoint | no | no | | Date filter | yes | after only | yes | via query operators | yes | yes | via query operators | | Domain filter | yes | yes | one native | via query operators | yes | yes | via query operators | | Max results per call | 100 | 50 | 50 | 50 | see docs | see docs | see docs | | Via Mux | yes | yes | yes | yes | no | no | no | Vendor sites: [exa.ai](https://exa.ai), [parallel.ai](https://parallel.ai), [keenable.ai](https://keenable.ai), [api-docs.deepseek.com](https://api-docs.deepseek.com/guides/search/), [tavily.com](https://www.tavily.com), [brave.com/search/api](https://brave.com/search/api/), [serper.dev](https://serper.dev). Filter support for Tavily, Brave and Serper is as described in their public documentation, and details change, so confirm against their docs. DeepSeek queries are limited to 128 characters including operators. ## Published prices For the four providers Mux routes to, the prices below are the vendors' list prices as passed through by Mux. Each request includes 10 results. Extra results beyond 10 cost the extra rate per result, except on Keenable and DeepSeek where they are free. DeepSeek bills only searches that return results, so an empty answer costs nothing. | Model | Per request (10 results) | Per 1,000 requests | Extra result | Max results | Typical latency | |---|---|---|---|---|---| | [exa/instant](https://getmux.io/models/exa/instant) | $0.0040 | $4.00 | $0.001 each | 100 | ~300 ms | | [exa/fast](https://getmux.io/models/exa/fast) | $0.0070 | $7.00 | $0.001 each | 100 | ~500 ms | | [exa/auto](https://getmux.io/models/exa/auto) | $0.0070 | $7.00 | $0.001 each | 100 | ~1 s | | [exa/deep](https://getmux.io/models/exa/deep) | $0.0120 | $12.00 | $0.001 each | 100 | ~5 s | | [parallel/turbo](https://getmux.io/models/parallel/turbo) | $0.0010 | $1.00 | $0.001 each | 50 | ~200 ms | | [parallel/fast](https://getmux.io/models/parallel/fast) | $0.0010 | $1.00 | $0.001 each | 50 | ~800 ms | | [parallel/basic](https://getmux.io/models/parallel/basic) | $0.0050 | $5.00 | $0.001 each | 50 | ~1 s | | [parallel/advanced](https://getmux.io/models/parallel/advanced) | $0.0050 | $5.00 | $0.001 each | 50 | ~3 s | | [keenable/pro](https://getmux.io/models/keenable/pro) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [keenable/realtime](https://getmux.io/models/keenable/realtime) | $0.0040 | $4.00 | none | 50 | ~700 ms | | [deepseek/search](https://getmux.io/models/deepseek/search) | $0.0008 | $0.75 | none | 50 | ~800 ms | For the others, as of October 2026: Tavily's [pricing page](https://www.tavily.com/pricing) lists pay-as-you-go at $0.008 per credit, with a [basic search costing 1 credit and an advanced search 2](https://docs.tavily.com/documentation/api-credits), so $8 or $16 per 1,000 searches at that rate, with monthly plans priced separately. Brave's [API page](https://brave.com/search/api/) lists its Search plan at $5 per 1,000 requests. Serper advertises 2,500 free queries on signup on its [site](https://serper.dev), with per-query rates available in your account. Each of these has its own comparison page: [Tavily alternatives](https://getmux.io/compare/tavily-alternatives), [Brave Search API alternatives](https://getmux.io/compare/brave-search-api-alternatives) and [SerpApi alternatives](https://getmux.io/compare/serpapi-alternatives), the last of which also covers Serper. ## Worked example: 1,000 agent runs Assume a research agent that does four searches per run, 10 results each, and runs 1,000 times. That is 4,000 searches. | Setup | Per search | Per 1,000 runs | |---|---|---| | All `deepseek/search` | $0.00075 | $3.00 | | All `parallel/fast` | $0.001 | $4.00 | | All `keenable/pro` | $0.004 | $16.00 | | All `exa/auto` | $0.007 | $28.00 | | 3 × `parallel/fast` + 1 × `exa/auto` with `content: "full"` | $0.001 / $0.007 | $10.00 | | All `exa/deep` | $0.012 | $48.00 | The mixed row is the usual shape: cheap lookups on [`parallel/fast`](https://getmux.io/models/parallel/fast) for discovery, one stronger search with full text on [`exa/auto`](https://getmux.io/models/exa/auto) when the agent has narrowed down what it wants. If the final step asks for 25 results on `exa/auto`, that call becomes $0.007 plus 15 × $0.001, or $0.022, and the row rises to $25.00. For monthly totals at 1,000 to 1,000,000 searches per model, see [web search cost per agent run](https://getmux.io/guides/web-search-cost-per-agent-run). Mux adds no per-request markup. Buying credits costs 5.5% of the purchase with a minimum of $0.80, so $50 buys $47.25 of credits and the mixed setup above runs about 4,700 times on that. Here is the mixed setup as code: ```ts import { Mux } from "getmux"; const mux = new Mux(); async function discover(q: string) { return mux.search({ query: q, models: ["parallel/fast", "keenable/pro"], maxResults: 10 }); } async function readDeep(q: string, domains: string[]) { return mux.search({ query: q, models: ["exa/auto", "parallel/advanced"], maxResults: 10, includeDomains: domains, content: "full", }); } ``` ```python from getmux import Mux mux = Mux() def discover(q: str): return mux.search(q, models=["parallel/fast", "keenable/pro"], max_results=10) def read_deep(q: str, domains: list[str]): return mux.search( q, models=["exa/auto", "parallel/advanced"], max_results=10, include_domains=domains, content="full", ) ``` Every response carries `usage.cost_usd`, so you can log the real spend per run instead of estimating it. ## Where Mux fits, and where it does not Mux is one key, one bill and one response shape across Exa, Parallel and Keenable, with ordered fallbacks, automatic refunds for failed attempts and per-key budgets. It fits when you want to compare providers without three integrations, or want failover without building it. It is not a fit if the provider you want is Tavily, Brave, Serper or another vendor Mux does not route to, if you need a crawl or extract endpoint, or if you run one provider at a volume where a negotiated direct contract beats list price. In those cases go direct. With [BYOK](https://getmux.io/docs/byok) you can bring your own vendor key and keep Mux for routing. ## FAQ ### What is the most important feature in a web search API for AI agents? Predictable behavior under load: stable result shape, sub-second tiers, clear rate limits, and a plan for failure. Price matters once those are in place, because an agent fleet multiplies every per-call cost by thousands. ### How much does web search cost per agent run? At list prices through Mux, a run with four 10-result searches costs between $0.004 on `parallel/fast` and $0.048 on `exa/deep`. A mixed plan of three cheap searches and one full-text search is about $0.01 per run. ### Do I need full page text or are snippets enough? For ranking, citation and deciding what to open next, snippets are enough. For summarizing or extracting facts from a page, you want full text. Only Exa returns it inline through Mux; Tavily sells it through an extract endpoint. ### How do I stop one agent from spending the whole budget? Give each agent its own API key with a USD limit and a daily, weekly or monthly reset. Requests over the limit return `402 key_limit_exceeded` and are not charged. To make your first call and see real costs and latencies, start at the [quickstart](https://getmux.io/docs/quickstart). --- # Changelog > What's new in Mux: API changes, pricing rules, SDK releases and dashboard features, newest first. Canonical: https://getmux.io/changelog. RSS: https://getmux.io/feed.xml ## 2026-10-20: Markdown for every page, new comparisons, about page - Model pages, the models index, pricing and the changelog now have markdown twins at the same URL with .md appended (for example /models/exa/auto.md), and any of those pages answers with markdown when requested with Accept: text/markdown. llms-full.txt includes the model pages and pricing. - Four new comparisons: Exa vs Tavily, Parallel vs Exa, Brave Search API alternatives and SerpApi alternatives, with prices checked against each vendor's public page on October 20, 2026. Existing guides link to each other and to the model pages they discuss. - New /about page. Docs and model pages show when they were last updated, articles carry a byline, and the price table can be sorted by model or price. ## 2026-10-19: DeepSeek search - New model deepseek/search: DeepSeek's search API at its list price of $0.75 per 1,000 requests, flat for 1 to 50 results, with a model-oriented summary as each result's snippet and full page text with content: "full". A search that returns no results is free. - DeepSeek queries are limited to 128 characters. Domain and date filters are sent as query operators when they fit and are always re-checked by Mux; a longer query fails that attempt without charge and falls through to the next model in your list. - Bring your own DeepSeek key with PUT /v1/provider-keys/deepseek. provider_options accepts ori_question and localization for DeepSeek. ## 2026-10-18: Pricing page, per-model pages, guides and comparisons - New /pricing page with worked monthly examples, purchase-fee table and FAQ, and a /models index with a page per model (price per request, cost at volume, provider behaviour, code samples), for example /models/exa/auto. - Guides (/guides) covering framework integrations (Vercel AI SDK, LangChain, OpenAI Agents SDK), fallbacks, per-key spend limits and cost budgeting, and comparisons (/compare) of Exa, Parallel, Keenable and other search APIs. - Every docs page, guide and comparison is also served as markdown at the same URL with .md appended, and llms.txt now links to those. An RSS feed of changelog entries and new articles is at /feed.xml. - Docs headings have anchor links, previous/next follow the sidebar order, and pages carry canonical URLs, social cards and structured data. ## 2026-10-15: provider_options is now allow-listed; timeouts keep the base price - provider_options accepts a fixed set of options per provider (Exa: category, userLocation, moderation, includeText, excludeText; Parallel: max_chars_total, client_model, advanced_settings.excerpt_settings and .location). Fields that change what a search costs or that Mux uses for filtering can no longer be set or overridden; such requests get 400 invalid_request. - A provider call that times out (15 s) now keeps the model's base price instead of being refunded in full, because the provider has normally completed and billed the search by then. The extra-results portion is still refunded, BYOK fees are always refunded, and the charge is shown as cost_usd on that attempt. - Extra results are now settled on the count the provider returned (capped at what you asked for), not on the count left after Mux's own post-filters. Use natively supported filters to keep the two the same. - Sign-in: a Google account can no longer be linked by email to an existing account that already belongs to a different Google identity. Such a sign-in is refused with an explanation. - Security headers (CSP, HSTS, frame-ancestors) on every page; analytics scripts no longer load on signed-in pages. - Search-only API keys now see only their own requests in usage and activity; the ledger and checkout need a management key. - BYOK fees count against the key's credit limit. Card refunds and disputes take the purchased credits back (ledger kind: chargeback). - Provider errors in responses and activity are now short, stable messages; full upstream details stay in Mux's logs. - New error code 403 account_suspended, returned for every key on an account Mux has suspended; the dashboard shows the reason. - Admin dashboard for operators, sitemap.xml, and a fix to robots.txt. ## 2026-10-13: Mux is live - One API for Exa, Parallel, Keenable and DeepSeek search: POST /v1/search with automatic fallbacks across models. - Per-key spend limits (daily, weekly or monthly) enforced atomically with the balance debit. - Prepaid credits through Stripe, with automatic refunds when a provider call fails. - Bring your own provider keys, free for the first 1M requests a month. - TypeScript and Python SDKs, API reference docs, and an llms.txt for coding assistants. - Dashboard with usage, activity, API keys, provider keys and credits. Sign in with Google.