# Doubao Web Search Custom

This document describes how to call the **Doubao Web Search Custom** model through ModelVerse, including its request and response parameters.

The Web Search API uses the unified endpoint `POST /v1/web_search`. The `model` field selects the provider and is removed before the request is forwarded. All other fields are native Doubao Web Search parameters. Requests and responses are passed through without normalization, so use the fields exactly as documented below.

> ⚠️ Doubao Web Search fields use **PascalCase** names such as `Query`, `Count`, and `Filter`. Field names are case-sensitive.

## Supported Model

| Model | Description |
| :--- | :--- |
| `doubao-web-search-custom` | Doubao Web Search Custom supports web and image search, page-content extraction, time and site filters, and query rewriting. It is suited to searches for content in China. |

## Endpoint

`https://api.umodelverse.ai/v1/web_search`

- **Method**: `POST`
- **Authentication**: `Authorization: Bearer <platform_api_key>`
- **Response mode**: **Non-streaming**; returns the native Doubao JSON response in a single response.

## Request Parameters

| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `model` | string | Yes | Must be `doubao-web-search-custom`; used for routing and removed before forwarding |
| `Query` | string | Yes | Search query, 1–100 characters |
| `SearchType` | string | Yes | Search type: `web` or `image` |
| `Count` | int | No | Number of results: up to 50 for `web` or 5 for `image`; default: `10` |
| `Filter` | object | No | Filtering conditions; see [Filter Object](#filter-object) |
| `QueryControl` | object | No | Retrieval controls; see [QueryControl Object](#querycontrol-object) |
| `ContentFormats` | string | No | Page-content format: `text` or `markdown` |
| `Industry` | string | No | Vertical search industry: `finance`, `game`, `health`, or `gov` |

### Filter Object

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `NeedContent` | bool | No | Whether to return the page content |
| `NeedUrl` | bool | No | Whether to return the result URL |
| `Sites` | list\<string\> | No | Site allowlist; return results only from the specified sites |
| `BlockHosts` | list\<string\> | No | Site blocklist; exclude results from the specified sites |
| `TimeRange` | string | No | Time range: `OneDay`, `OneWeek`, `OneMonth`, `OneYear`, or a date range |

### QueryControl Object

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `QueryRewrite` | bool | No | Whether to use an LLM to rewrite `Query` and improve recall |

## Request Examples

⚠️ On Windows, we recommend using Postman or another API client.

### curl

```bash
curl -X POST https://api.umodelverse.ai/v1/web_search \
  -H "Authorization: Bearer $MODELVERSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "doubao-web-search-custom",
    "Query": "latest Beijing travel guide",
    "SearchType": "web",
    "Count": 10,
    "Filter": {
      "NeedContent": false,
      "NeedUrl": true,
      "TimeRange": "OneYear"
    },
    "QueryControl": {
      "QueryRewrite": true
    },
    "ContentFormats": "markdown"
  }'
```

### Python

```python
import os
import requests

resp = requests.post(
    "https://api.umodelverse.ai/v1/web_search",
    headers={
        "Authorization": f"Bearer {os.getenv('MODELVERSE_API_KEY')}",
        "Content-Type": "application/json",
    },
    json={
        "model": "doubao-web-search-custom",
        "Query": "latest Beijing travel guide",
        "SearchType": "web",
        "Count": 10,
        "Filter": {
            "NeedContent": False,
            "NeedUrl": True,
            "TimeRange": "OneYear",
        },
        "QueryControl": {"QueryRewrite": True},
        "ContentFormats": "markdown",
    },
)
data = resp.json()
for item in data.get("Result", {}).get("WebResults", []):
    print(item["Title"], item["Url"])
```

## Response Format

The endpoint returns `application/json` using the native Doubao Web Search response structure without normalization. The main payload is in `Result`.

### Output Fields

| Parameter | Type | Description |
| :--- | :--- | :--- |
| `ResponseMetadata` | object | Request metadata |
| `ResponseMetadata.RequestId` | string | Unique request identifier used for troubleshooting |
| `ResponseMetadata.Error` | object | Business error information; `Code` is an empty string on success |
| `ResponseMetadata.Error.Code` | string | Business error code; empty on success |
| `ResponseMetadata.Error.Message` | string | Business error description; empty on success |
| `Result` | object | Search results |
| `Result.ResultCount` | int | Number of returned results |
| `Result.WebResults` | array | Web result list returned when `SearchType=web`; see the table below |
| `Result.ImageResults` | array | Image result list returned when `SearchType=image` |
| `Result.SearchContext` | object | Search context containing `OriginQuery` and `SearchType` |
| `Result.TimeCost` | int | Upstream processing time in milliseconds |
| `Result.LogId` | string | Upstream log ID |

**Fields in each `WebResults[]` item:**

| Field | Type | Description |
| :--- | :--- | :--- |
| `Title` | string | Page title |
| `Url` | string | Page URL |
| `Snippet` | string | Result snippet |
| `Content` | string | Page content, returned when `Filter.NeedContent=true` |
| `Position` | int | Result ranking position |

### Response Example

```json
{
  "ResponseMetadata": {
    "RequestId": "...",
    "Error": { "Code": "", "Message": "" }
  },
  "Result": {
    "ResultCount": 10,
    "WebResults": [
      {
        "Title": "...",
        "Url": "https://...",
        "Snippet": "...",
        "Content": "...",
        "Position": 1
      }
    ],
    "ImageResults": [],
    "SearchContext": {
      "OriginQuery": "latest Beijing travel guide",
      "SearchType": "web"
    },
    "TimeCost": 123,
    "LogId": "..."
  }
}
```

## Determining Success or Failure

- HTTP `200` **and** an empty `ResponseMetadata.Error.Code`: **Success**; billed as one request.
- HTTP `200` with a non-empty `ResponseMetadata.Error.Code`: **Business failure**; not billed.
- Non-`200` upstream response (4xx/5xx): The gateway passes through the error status; not billed.

## Error Response

For gateway-side parameter or authentication errors, the API returns a standard JSON error response:

```json
{
  "error": {
    "message": "Error description",
    "type": "invalid_request_error",
    "code": "error_code",
    "param": "<request ID for feedback or troubleshooting>"
  }
}
```

Common error scenarios:

| Scenario | Result |
| :--- | :--- |
| Unknown `model` or no adapter is bound | Parameter error with `unsupported web search model` |
| Invalid API key or no permission to use the model | Authentication or permission error |
| A required field such as `Query` or `SearchType` is missing | Upstream business error; the gateway marks the request as failed and does not bill it |

## Notes

1. **Per-request billing**: Each successful search is billed once at the model price. Upstream business failures and non-2xx responses are not billed.
2. **Field casing**: Doubao Web Search uses PascalCase names such as `Query`, `Count`, and `Filter`, unlike OpenAI-style lowercase field names. Do not mix the two styles.
3. **No normalization**: Requests and responses are passed through unchanged. Field structures vary by `model`; use the fields defined for the selected model.
4. **Result limits**: `Count` must not exceed 50 for `web` or 5 for `image`.
5. **Query length**: `Query` must contain 1–100 characters. Truncate longer queries before sending them.
