X-API-Key: sk_live_xxxxxxxxxxxx
# or
Authorization: Bearer sk_live_xxxxxxxxxxxx
POST https://webtest.cholab.kr/v1/search
curl -X POST https://webtest.cholab.kr/v1/search \
-H "X-API-Key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{"q":"openwrt captive portal","engine":"auto","limit":10}'
GET /v1/credits
{"credits": 4999, "total_granted": 5000, "email": "you@example.com"}
| q | string | — |
| engine | auto | google | duckduckgo | bing | brave | all | auto |
| type | web | images | news | videos | web |
| limit | 1–100 | 10 |
| page | 1–10 | 1 |
| lang | ko · en · ja · zh · … | — |
| region | kr · us · jp · cn · … | — |
| safe | off | moderate | strict | moderate |
| site | domain | — |
| exclude_site | a.com,b.com | — |
| filetype | pdf · docx · … | — |
| after / before | YYYY-MM-DD | — |
| exact | string | — |
| sort | relevance | date | relevance |
| deduplicate | true | false | true |
| mode | urls | minimal | standard | full | custom | raw | standard |
| fields | title,url,snippet,rank,engine,date,… | — |
| timeout_ms | 500–10000 | 4000 |
| cache | use | refresh | bypass | use |
| response | first | complete | complete |
| parallel | true | false | true |
| format | json | llm | json |
| group_by | merged | engine | — |
GET /api/capabilities
mode=urls → ["https://a.com/", "https://b.com/"]
mode=minimal → [{"title":"...","url":"..."}]
mode=standard → [{"title":"...","url":"...","snippet":"...","rank":1,"engine":"brave"}]
mode=custom → fields=["title","url"] → [{"title":"...","url":"..."}]
mode=raw → {"brave": { ...engine raw... }}
{
"query": { "q": "...", "engine": "auto", "limit": 10 },
"results": [ { "title": "...", "url": "...", "snippet": "...",
"rank": 1, "engine": "brave" } ],
"meta": {
"engine_used": ["brave"],
"engine_selected_by": "auto",
"engines": {
"google": { "status": "error",
"error": { "code": "COOLDOWN", "message": "..." } },
"brave": { "status": "ok", "count": 10, "elapsed_ms": 704, "cached": false }
},
"total": 10, "elapsed_ms": 712, "cached": false, "partial": false,
"warnings": []
},
"credits": 4999
}
{ "param": "before", "engine": "duckduckgo",
"reason": "DuckDuckGo HTML endpoint does not support an end date",
"effect": "ignored" }
| HTTP | code | |
|---|---|---|
| 400 | MISSING_QUERY | q is empty |
| 401 | MISSING_API_KEY / INVALID_API_KEY | key missing or invalid |
| 402 | INSUFFICIENT_CREDITS | not enough credits |
| 429 | DEMO_LIMIT | demo quota exceeded for today |
| 502 | ALL_ENGINES_FAILED | no engine returned results (not charged) |
cURL
curl -s -X POST https://webtest.cholab.kr/v1/search \
-H "X-API-Key: $CHOLAB_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"openwrt captive portal","engine":"auto","limit":10,
"lang":"ko","region":"kr","site":"openwrt.org"}' \
| jq '.results[] | {title, url}'
Python
import httpx
KEY = "sk_live_..."
def search(q, **kw):
r = httpx.post(
"https://webtest.cholab.kr/v1/search",
headers={"X-API-Key": KEY},
json={"q": q, "engine": "auto", "limit": 10, **kw},
timeout=25,
)
r.raise_for_status()
data = r.json()
for w in data["meta"]["warnings"]:
print("[warn]", w["param"], w["reason"])
return data["results"], data["meta"]
results, meta = search("openwrt captive portal")
print(meta["engine_used"], meta["elapsed_ms"], "ms")
for item in results:
print(item["rank"], item["title"], item["url"])
Node.js
const KEY = process.env.CHOLAB_KEY;
async function search(q, opts = {}) {
const res = await fetch("https://webtest.cholab.kr/v1/search", {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ q, engine: "auto", limit: 10, ...opts }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const { results, meta, credits } = await res.json();
console.log(meta.elapsed_ms + "ms", "credits:", credits);
return results;
}
const rows = await search("openwrt captive portal", { limit: 5 });
rows.forEach(r => console.log(r.rank, r.title));
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
type Result struct {
Title string `json:"title"`
URL string `json:"url"`
Snippet string `json:"snippet"`
Rank int `json:"rank"`
Engine string `json:"engine"`
}
type Response struct {
Results []Result `json:"results"`
Meta struct {
Total int `json:"total"`
ElapsedMs int `json:"elapsed_ms"`
EngineUsed []string `json:"engine_used"`
} `json:"meta"`
Credits int `json:"credits"`
}
func main() {
body, _ := json.Marshal(map[string]any{
"q": "openwrt captive portal", "engine": "auto", "limit": 10,
})
req, _ := http.NewRequest("POST", "https://webtest.cholab.kr/v1/search",
bytes.NewReader(body))
req.Header.Set("X-API-Key", os.Getenv("CHOLAB_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var out Response
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
panic(err)
}
fmt.Println("credits:", out.Credits, "|", out.Meta.ElapsedMs, "ms")
for _, r := range out.Results {
fmt.Printf("%d. %s — %s\n", r.Rank, r.Title, r.URL)
}
}
PHP
<?php
$ch = curl_init('https://webtest.cholab.kr/v1/search');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . getenv('CHOLAB_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'q' => 'openwrt captive portal', 'engine' => 'auto', 'limit' => 10,
]),
CURLOPT_TIMEOUT => 25,
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($data['results'] as $r) {
echo $r['rank'] . '. ' . $r['title'] . ' — ' . $r['url'] . PHP_EOL;
}
Java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class SearchExample {
public static void main(String[] args) throws Exception {
String body = """
{"q":"openwrt captive portal","engine":"auto","limit":10}
""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://webtest.cholab.kr/v1/search"))
.header("X-API-Key", System.getenv("CHOLAB_KEY"))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(25))
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> res = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode());
System.out.println(res.body());
}
}
Ruby
require 'net/http'
require 'json'
require 'uri'
uri = URI('https://webtest.cholab.kr/v1/search')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV.fetch('CHOLAB_KEY')
req['Content-Type'] = 'application/json'
req.body = { q: 'openwrt captive portal', engine: 'auto', limit: 10 }.to_json
res = http.request(req)
data = JSON.parse(res.body)
data['results'].each do |r|
puts "#{r['rank']}. #{r['title']} — #{r['url']}"
end