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"}

qstring—
engineauto | google | duckduckgo | bing | brave | allauto
typeweb | images | news | videosweb
limit1–10010
page1–101
langko · en · ja · zh · …—
regionkr · us · jp · cn · …—
safeoff | moderate | strictmoderate
sitedomain—
exclude_sitea.com,b.com—
filetypepdf · docx · …—
after / beforeYYYY-MM-DD—
exactstring—
sortrelevance | daterelevance
deduplicatetrue | falsetrue
modeurls | minimal | standard | full | custom | rawstandard
fieldstitle,url,snippet,rank,engine,date,…—
timeout_ms500–100004000
cacheuse | refresh | bypassuse
responsefirst | completecomplete
paralleltrue | falsetrue
formatjson | llmjson
group_bymerged | 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" }

HTTPcode
400MISSING_QUERYq is empty
401MISSING_API_KEY / INVALID_API_KEYkey missing or invalid
402INSUFFICIENT_CREDITSnot enough credits
429DEMO_LIMITdemo quota exceeded for today
502ALL_ENGINES_FAILEDno 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

bravebingduckduckgogoogle
limit20501030
page5513
typeweb/news/images/videosweb/news/images/videos webweb/news/images/videos

cho.terry77777@gmail.com