Search API
POST/v1/search
Search the web and vertical data sources through one endpoint. General search only needs query. For a vertical capability, discover its tag and parameter definitions first. The gateway routes the request, combines sources and reranks results.
Request examples
Submit query. The gateway routes the request, combines sources and reranks results.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
curl -X POST https://api.anysearch.com/v1/search \
-H "Authorization: Bearer YOUR_ANYSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Global AI Semiconductor Industry Analysis",
"max_results": 5
}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
import os
import requests
response = requests.post(
"https://api.anysearch.com/v1/search",
headers={
"Authorization": f"Bearer {os.environ['ANYSEARCH_API_KEY']}",
},
json={
"query": "Global AI Semiconductor Industry Analysis",
"max_results": 5,
},
timeout=30,
)
if not response.ok:
request_id = response.headers.get("X-Request-ID", "unknown")
raise RuntimeError(
f"Request failed: HTTP {response.status_code}; request_id={request_id}"
)
result = response.json()
print(result["data"])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
const response = await fetch("https://api.anysearch.com/v1/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANYSEARCH_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "Global AI Semiconductor Industry Analysis",
max_results: 5,
}),
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
const requestId = response.headers.get("X-Request-ID") ?? "unknown";
throw new Error(
`Request failed: HTTP ${response.status}; request_id=${requestId}`
);
}
const result = await response.json();
console.log(result.data);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
)
func main() {
req, err := http.NewRequest(
http.MethodPost,
"https://api.anysearch.com/v1/search",
strings.NewReader(`{
"query": "Global AI Semiconductor Industry Analysis",
"max_results": 5
}`),
)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("ANYSEARCH_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
requestID := resp.Header.Get("X-Request-ID")
if requestID == "" {
requestID = "unknown"
}
panic(fmt.Sprintf(
"Request failed: HTTP %d; request_id=%s",
resp.StatusCode, requestID,
))
}
var result struct {
Data json.RawMessage `json:"data"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
panic(err)
}
fmt.Println(string(result.Data))
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
use reqwest::blocking::Client;
use serde_json::{json, Value};
use std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> {
let api_key = env::var("ANYSEARCH_API_KEY")?;
let client = Client::builder()
.timeout(Duration::from_secs(30))
.build()?;
let response = client
.post("https://api.anysearch.com/v1/search")
.bearer_auth(api_key)
.json(&json!({
"query": "Global AI Semiconductor Industry Analysis",
"max_results": 5,
}))
.send()?;
if !response.status().is_success() {
let request_id = response
.headers()
.get("X-Request-ID")
.and_then(|value| value.to_str().ok())
.unwrap_or("unknown");
return Err(format!(
"Request failed: HTTP {}; request_id={request_id}",
response.status()
)
.into());
}
let result: Value = response.json()?;
println!("{}", result["data"]);
Ok(())
}
Use the discovered finance.quote capability and its required type: "stock" parameter.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
curl -X POST https://api.anysearch.com/v1/search \
-H "Authorization: Bearer YOUR_ANYSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Apple stock price",
"tag": "finance.quote",
"params": {
"type": "stock"
},
"max_results": 5
}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
# Install: pip install requests
import os
import requests
payload = {
"query": "Apple stock price",
"tag": "finance.quote",
"params": {
"type": "stock"
},
"max_results": 5
}
response = requests.post(
"https://api.anysearch.com/v1/search",
headers={
"Authorization": "Bearer " + os.environ["ANYSEARCH_API_KEY"],
},
json=payload,
timeout=30,
)
if not response.ok:
# Do not log the response body: HTTP 402 may contain credentials.
print({"status": response.status_code,
"request_id": response.headers.get("X-Request-ID")})
raise SystemExit(1)
result = response.json()
print(result["data"])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
// Node.js 18+; set ANYSEARCH_API_KEY before running.
const response = await fetch("https://api.anysearch.com/v1/search", {
method: "POST",
headers: {
"Authorization": "Bearer " + process.env.ANYSEARCH_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
"query": "Apple stock price",
"tag": "finance.quote",
"params": {
"type": "stock"
},
"max_results": 5
}),
signal: AbortSignal.timeout(30_000)
});
if (!response.ok) {
// Do not log the response body: HTTP 402 may contain credentials.
throw new Error("HTTP " + response.status +
"; request_id=" + response.headers.get("X-Request-ID"));
}
const result = await response.json();
console.log(result.data);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() error {
key := os.Getenv("ANYSEARCH_API_KEY")
if key == "" {
return fmt.Errorf("set ANYSEARCH_API_KEY before running")
}
payload := strings.NewReader(`{
"query": "Apple stock price",
"tag": "finance.quote",
"params": {
"type": "stock"
},
"max_results": 5
}`)
request, err := http.NewRequest("POST", "https://api.anysearch.com/v1/search", payload)
if err != nil {
return err
}
request.Header.Set("Authorization", "Bearer " + key)
request.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
// HTTP 402 bodies may contain credentials; keep them out of logs.
return fmt.Errorf("HTTP %d; request_id=%s",
response.StatusCode, response.Header.Get("X-Request-ID"))
}
var result map[string]json.RawMessage
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
return err
}
fmt.Println(string(result["data"]))
return nil
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
// Dependencies:
// cargo add reqwest --features blocking,json
// cargo add serde_json
use reqwest::blocking::Client;
use serde_json::{json, Value};
use std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> {
let key = env::var("ANYSEARCH_API_KEY")?;
let client = Client::builder()
.timeout(Duration::from_secs(30))
.build()?;
let response = client
.post("https://api.anysearch.com/v1/search")
.bearer_auth(key)
.json(&json!({
"query": "Apple stock price",
"tag": "finance.quote",
"params": {
"type": "stock"
},
"max_results": 5
}))
.send()?;
if !response.status().is_success() {
// HTTP 402 bodies may contain credentials; keep them out of logs.
let request_id = response.headers().get("X-Request-ID")
.and_then(|value| value.to_str().ok()).unwrap_or("unknown");
return Err(format!("HTTP {}; request_id={}",
response.status(), request_id).into());
}
let result: Value = response.json()?;
println!("{}", result["data"]);
Ok(())
}
Authentication and example environment
Examples use Authorization: Bearer YOUR_ANYSEARCH_API_KEY. Python, JavaScript, Go and Rust read the ANYSEARCH_API_KEY environment variable. cURL uses Bash syntax; use Git Bash or WSL on Windows. Code timeouts are client-side only.
Using tag and params
Find a capability by tag, purpose or parameter name →
| Item | How to fill it |
|---|---|
| tag | Use one complete tag, not only its domain. |
| params | Use a JSON object with parameter names defined by that tag. Omit it if there are no dedicated parameters. |
| Required and conditional parameters | Read both required flags and descriptions. required=false can still have a conditional requirement. |
| Types, allowed values and defaults | The definitions provide description, required and optional sort_order. Do not infer separate schema fields. |
| Applicability | Respect upstream-specific, summary-only and future-filter limitations. |
A minimal general search can omit tag and only submit query.
When to include tag and params
| Scenario | tag | params | Flow |
|---|---|---|---|
| General search | Omit | Omit | Submit query for intent-based routing. |
| Specific vertical capability | Use a discovered sub_domain | Follow its definition | Discover → read parameters → search. |
Request parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| query | string | Required | — | A non-empty search query. |
| max_results | int | Optional | 10 | Number of results, from 1 to 10. |
| tag | string | Optional | — | One complete capability tag in {domain}.{sub_domain} form. Read it from the capability directory or discovery response. |
| zone | string | Optional | — | Region: cn or intl. |
| language | string | Optional | — | Preferred language, such as zh-CN or en. |
| params | object | Optional | — | Capability-specific parameters. Follow the returned names, descriptions and required flags. |
| format | string | Optional | — | json or markdown. Markdown changes result content; the outer response is still JSON. |
— means no documented default or not applicable.
Response fields
Markdown content
format: "markdown" changes data.results[].content; the outer response remains JSON.
| Field | Description |
|---|---|
| data.results | Array of search results. |
| data.results[].title | Always present; can be an empty string if the source has no title. |
| data.results[].url | Source URL. |
| data.results[].snippet | Summary; omitted if unavailable. |
| data.results[].content | Cleaned content; omitted if unavailable. |
| data.metadata.total_results | Actual number of returned results. |
| data.metadata.search_time_ms | Upstream search time in milliseconds. |
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
{
"code": 0,
"message": "success",
"request_id": "7d6f4e91-2a83-4c5b-9f10-6e8a3d27b541",
"data": {
"results": [
{
"title": "Example source",
"url": "https://example.com/article",
"snippet": "A short source summary.",
"content": "Cleaned page content for the example."
}
],
"metadata": {
"total_results": 1,
"search_time_ms": 312
}
}
}
Error codes
HTTP status codes and their meanings. error_code is optional; inspect the actual response.
| HTTP status | Meaning |
|---|---|
| 400 | Invalid request body or fields, missing query, or an upstream client error. |
| 401 | Invalid Authorization or API Key. |
| 402 | Quota exhausted. An anonymous request may receive generated credentials in message. |
| 403 | API Key expired, account disabled, or access forbidden. |
| 415 | Missing Content-Type or a value other than application/json. |
| 429 | API Key, user, or anonymous IP rate limit exceeded. |
| 502 | Search service unavailable or an invalid upstream response. |
- 1
- 2
- 3
- 4
- 5
{
"code": -1,
"message": "Query is required.",
"request_id": "7d6f4e91-2a83-4c5b-9f10-6e8a3d27b541"
}
Anonymous quota response
When an anonymous request exceeds the daily free quota, the rejected request is not executed. After automatic account creation completes, the multi-line HTTP 402 message can contain a username, password and API Key. Account creation may briefly still be in progress. Once complete credentials are available, use the Bearer API Key to resubmit the original request.
- 1
- 2
- 3
- 4
- 5
{
"code": -1,
"message": "Your account and API key have been automatically generated. Use the API key below to continue.\nusername=<USERNAME>\npassword=<PASSWORD>\napi_key=<API_KEY>",
"request_id": "7d6f4e91-2a83-4c5b-9f10-6e8a3d27b541"
}
Responses may contain credentials
HTTP 402 may contain a password and API Key. Do not pass the raw response to routine logs, analytics, tracing, tickets or model context. Retain only the HTTP status and request_id for diagnostics.
FAQ
Is tag required?
No. General search only needs query. Discover a vertical capability before specifying its tag and parameters.
Can snippet or content be missing?
Yes. Both fields can be omitted if unavailable. Clients should handle missing values.