> For the complete documentation index, see [llms.txt](https://docs.meganova.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.meganova.ai/api-reference/platform-api/search.md).

# Web Search

Managed web search and URL extraction via a single MegaNova API key.

MegaNova's managed web search service exposes two complementary endpoints for agents, retrieval pipelines, and research tools:

| Endpoint                                                                                      | Purpose                                                                                   |
| --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [`POST /serverless/search`](/api-reference/platform-api/search/search-query.md)               | Natural-language search with built-in fallback, query expansion, and content enrichment   |
| [`POST /serverless/fetch`](/api-reference/platform-api/search/fetch-url.md)                   | Convert any URL to clean markdown                                                         |
| [`GET /serverless/search/usage`](/api-reference/platform-api/search/get-search-usage.md)      | Today's quota & spend snapshot for the calling user                                       |
| [`POST /serverless/search/group`](/api-reference/platform-api/search/create-request-group.md) | Mint a signed request group so one prompt's fan-out bills once — **Token Plan keys only** |

A single MegaNova `sk-*` API key replaces the multiple vendor keys (search engines, content extractors, etc.) that a typical agent stack used to need. One billing relationship, one rate-limit envelope, one auth surface.

> **Base URL** — every request goes to `{API_URL}` = `https://api.meganova.ai/api/v1`, the same base as the rest of the [API Reference](/api-reference.md). Authenticate with your MegaNova API key (`sk-...`) in the `Authorization: Bearer <key>` header.
>
> `POST /v1/serverless/search` also works, as an alias for clients already pointed at an OpenAI-style `.../v1` base. **The alias covers `/search` only** — `/fetch`, `/search/usage`, and `/search/group` are served under `/api/v1` and return `404` on `/v1`.

## Quick start (5 minutes)

Get an API key from the [user dashboard](https://console.meganova.ai/) (account must hold ENGINEER\_TIER\_2 — auto-granted on first $1+ deposit), then:

```bash
# 1. Confirm the endpoint is reachable + your key has the right tier
curl https://api.meganova.ai/api/v1/serverless/search/usage \
  -H "Authorization: Bearer sk-..."
# → 200 with quota_daily=50, fetch_quota_daily=50, ... means you're good

# 2. Run your first search
curl -X POST https://api.meganova.ai/api/v1/serverless/search \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"query":"latest LLM benchmarks 2026","max_results":3}'
# → 200 with results[]; usage.overage_charged_usd=0.0 confirms you're inside free quota
```

If both calls return `200`, integration is working. If you got `401` check the key, `403` means the user is on the free tier (top up $1+, **or** use a Token Plan member key, which is entitled regardless of tier), `404` means a path typo.

## How to integrate

Most callers fall into one of these patterns:

| Use case                                 | Pattern                                                                                                                                                                                                                                              |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User asks a question                     | Single [`/search`](/api-reference/platform-api/search/search-query.md) call, optionally with `enrich=true` to get full page text in one round-trip                                                                                                   |
| User pastes a URL ("summarize this")     | Single [`/fetch`](/api-reference/platform-api/search/fetch-url.md) call                                                                                                                                                                              |
| Agent / deep research / RAG ingestion    | [`/search`](/api-reference/platform-api/search/search-query.md) → pick top URLs → [`/fetch`](/api-reference/platform-api/search/fetch-url.md) for full text. Use `X-Request-Group-Id` to keep all sub-calls of one user prompt in **one** quota slot |
| Throttle yourself before hitting overage | Periodically poll [`/search/usage`](/api-reference/platform-api/search/get-search-usage.md) and react to `quota_remaining` / `fetch_quota_remaining`                                                                                                 |

### `/search` then `/fetch` — the typical agent recipe

```python
import os, uuid, requests

BASE = "https://api.meganova.ai/api/v1/serverless"
HEADERS = {"Authorization": f"Bearer {os.environ['MEGANOVA_API_KEY']}",
           "Content-Type": "application/json"}

def search(query, group_id=None, **kwargs):
    h = dict(HEADERS)
    if group_id: h["X-Request-Group-Id"] = group_id
    r = requests.post(f"{BASE}/search", headers=h,
                      json={"query": query, "enrich": False, **kwargs}, timeout=30)
    r.raise_for_status(); return r.json()["data"]

def fetch(url):
    r = requests.post(f"{BASE}/fetch", headers=HEADERS,
                      json={"url": url}, timeout=30)
    r.raise_for_status(); return r.json()["data"]

# One user prompt — all sub-calls share one quota slot via group_id
prompt_group = str(uuid.uuid4())
hits = search("Mistral 7B vs Llama 2 13B benchmarks", group_id=prompt_group)["results"]
docs = [fetch(h["url"])["markdown"] for h in hits[:3]]
# `docs` is now ready to feed into your LLM as context
```

> **Server-side only.** The `sk-*` key is user-level — never embed it in browser-side JavaScript, mobile app bundles, or other client-side code where users can read it. If a key leaks, revoke it immediately at the [user dashboard](https://console.meganova.ai/).

## Authentication & Tier

| Requirement     | Detail                                                                                                                                                                                                                      |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Header          | `Authorization: Bearer <sk-...>` — same key used for `/v1/chat/completions`, `/v1/images/generations`, etc.                                                                                                                 |
| Tier gate       | `ENGINEER_TIER_2` (auto-granted on first $1+ deposit). Lower tiers receive `403`.                                                                                                                                           |
| Token Plan keys | Entitled **regardless of tier**. A [Libra OS Token Plan](/enterprise/token-plan.md) member key passes the gate even on the free tier, because the entitlement comes from the subscription rather than from deposit history. |
| Key scope       | Any active key works; no separate "search" scope needed.                                                                                                                                                                    |
| Key issuance    | Create in the [user dashboard](https://console.meganova.ai/) under *API Keys*.                                                                                                                                              |

## Quota Model

`/search` and `/fetch` are tracked as **two independent quota dimensions**. Burning one does not affect the other.

| Endpoint  | Counted by                                                                                                                                         | Free / day | Overage              |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------------------- |
| `/search` | Distinct **prompt-groups** (see [`X-Request-Group-Id`](/api-reference/platform-api/search/search-query.md#x-request-group-id-fan-out-quota-dedup)) | 50         | **$0.002** per group |
| `/fetch`  | Raw successful calls                                                                                                                               | 50         | **$0.002** per call  |

### Token Plan keys bill differently

A [Libra OS Token Plan](/enterprise/token-plan.md) **member key** does not touch the daily free pool at all. Its searches and fetches draw on the subscription's monthly allowance at the same per-unit prices, and past the allowance they follow the plan's opt-in capped Extra Usage rather than the ordinary wallet overage.

Two consequences worth designing around:

* The `quota_*` fields in [`/search/usage`](/api-reference/platform-api/search/get-search-usage.md) describe the daily pool and will read `0` for plan traffic. The `token_plan` block in the same response is what reflects plan consumption.
* Entitlement is **per key**, not per account. A subscriber calling with an ordinary key is still billed from the daily pool, so both readings can be non-zero on the same day.

Plan keys should mint a [signed request group](/api-reference/platform-api/search/create-request-group.md) rather than sending a group id of their own — see that page for why an unsigned one earns nothing.

Both daily quotas reset at **UTC `00:00`**. Overage charges debit `credit_balance` in real time. Prices above are the current production defaults — they can be tuned without a code change, so always use [`/search/usage`](/api-reference/platform-api/search/get-search-usage.md) (`overage_price_per_query` and `fetch_overage_price_per_call` fields) if you need the live rate at runtime rather than hard-coding.

> **Failed calls do NOT debit.** If we return a `5xx` (all upstream sources exhausted) or `429` (rate-limited) or `402` (insufficient credit pre-check), no `credit_balance` charge is applied and no quota slot is consumed. Only successful `200` responses count. Feel free to retry on `5xx` without worrying about double-billing.

### Cost scenarios at default pricing

Helps size your integration. All scenarios assume current production defaults: $0.002 per `/search` prompt-group overage, $0.002 per `/fetch` call overage.

| Use case                                    | Daily volume                                                                                   | Monthly overage cost                                                     |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Personal project / dev integration          | ≤ 50 search + ≤ 50 fetch / day                                                                 | **$0** (entirely in free tier)                                           |
| Small SaaS                                  | \~300 search + \~200 fetch / day, **with** `X-Request-Group-Id`                                | search 250 × $0.002 × 30 + fetch 150 × $0.002 × 30 = **\~$24 / month**   |
| Agent / RAG pipeline                        | \~1k search + \~1k fetch / day, **with** `X-Request-Group-Id`                                  | 950 × $0.002 × 30 + 950 × $0.002 × 30 = **\~$114 / month**               |
| Same agent **without** `X-Request-Group-Id` | search count balloons to \~5× because each fan-out sub-call now counts as its own prompt-group | search 4750 × $0.002 × 30 + fetch 950 × $0.002 × 30 = **\~$342 / month** |

Cost predictability is one reason `X-Request-Group-Id` exists — see [Search Query → fan-out quota dedup](/api-reference/platform-api/search/search-query.md#x-request-group-id-fan-out-quota-dedup). Note `X-Request-Group-Id` only affects `/search` cost; `/fetch` always counts each call regardless.

## Operational Characteristics

| Property                                        | Value                                     |
| ----------------------------------------------- | ----------------------------------------- |
| Typical latency (`/search` with `enrich=true`)  | 3–10 s P50, 15 s P95                      |
| Typical latency (`/search` with `enrich=false`) | 1–3 s P50                                 |
| `/fetch` latency                                | 1–5 s                                     |
| Per-request size cap                            | `max_results` ≤ 20, `max_bytes` ≤ 128 KiB |
| Per-key concurrency cap                         | 20 (paid tier) / 50 (enterprise tier)     |
| Per-key RPS limit                               | 10 req/s                                  |

## Best Practices

1. **Always set `X-Request-Group-Id` for user-driven workflows.** Even single-call use cases benefit — if your code may later add fan-out, the cost story is already correct. Use a UUIDv4 per user-facing prompt.
2. **Set `enrich=false` when you only need link previews.** Cuts latency by 60–80% and reduces server load. Switch to `true` only when your downstream (e.g. an LLM) actually consumes the page text.
3. **Cache `request_id` from the response.** If a result looks wrong, support can use `request_id` to investigate.
4. **Don't pre-decompose your query.** Send the full natural-language question — the service handles query expansion for you. `"latest AI agent frameworks"` produces better results than splitting into `["latest AI", "agent frameworks", "2026"]`.
5. **Watch `usage.overage_charged_usd`** in each response — it's `0.0` while inside the free quota; once non-zero you're paying per call. Use [`/serverless/search/usage`](/api-reference/platform-api/search/get-search-usage.md) to drive an in-product meter.
6. **Treat `5xx` as non-fatal.** Falling back to your own provider keys (or returning "no results" to the user) is acceptable — and you're not charged.

## Errors

All errors follow OpenAI-style format:

```json
{
  "error": {
    "message": "Insufficient credits for search overage. Balance: $0.0010, required: $0.0020. Today's free quota (50/day) is exhausted.",
    "type":    "billing_error",
    "param":   null,
    "code":    "402"
  }
}
```

| Status | When                                                                                                   | What to do                                                                          |
| ------ | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `400`  | Invalid body — `query` missing/too long, `depth` not in `{standard, deep}`, `max_results` out of range | Fix the request shape                                                               |
| `401`  | Missing / invalid / revoked API key                                                                    | Check `Authorization` header                                                        |
| `402`  | `credit_balance` below overage unit price AND daily free quota exhausted                               | Top up credits or wait for UTC midnight reset                                       |
| `403`  | User below `ENGINEER_TIER_2`                                                                           | Make a $1+ deposit to unlock paid-tier features                                     |
| `429`  | Per-key in-flight cap or per-second rate limit hit. Response includes `Retry-After`                    | Backoff and retry                                                                   |
| `5xx`  | All upstream sources exhausted                                                                         | Caller can fall back to its own search keys; we do **not** charge overage for `5xx` |

## Support

* **Bug reports, feature requests, and account / billing questions** — submit a ticket from the [user dashboard](https://console.meganova.ai/) via the **Help** button in the bottom-right, or email <support@meganova.ai>. Include the `request_id` from the response (or from the `X-Request-Id` response header for non-200s) so we can correlate to server-side logs.
* **Status / outages** — public status page (link TBD).

## Related

* [Search query](/api-reference/platform-api/search/search-query.md) — `POST /serverless/search`
* [Fetch URL](/api-reference/platform-api/search/fetch-url.md) — `POST /serverless/fetch`
* [Get search usage](/api-reference/platform-api/search/get-search-usage.md) — `GET /serverless/search/usage`
* [User Credit Balance](/api-reference/platform-api/get-credit-balance.md)
* [Errors & Response Conventions](/api-reference/platform-api/errors.md)
