Guides
Web search
One call that searches the live web and returns links with the matching text. It is built for agents: the request and response follow the Exa search shape, so tools that already speak to Exa (including DeepSeek Harness) work by changing only the base URL.
At a glance
| Field | Detail |
|---|---|
| Endpoint | POST /v1/search |
| Auth | Your inference key, as a Bearer token (the same ub-gw-… key). |
| Billing | Per search, from your balance. No free tier. A search that fails is not charged. |
| Search mode | Fast: quick results for agents that search many times per task. |
| Availability | Switched on by unoblox; while it is off every call returns UB-GW-301 and nothing is charged. |
Call it
# curl
curl -sS https://api.unoblox.ai/v1/search \
-H "Authorization: Bearer $UNOBLOX_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "latest Rust async runtime benchmarks", "numResults": 5 }'{
"requestId": "gen_01j9x...",
"results": [
{
"id": "https://example.org/rust-async-benchmarks",
"url": "https://example.org/rust-async-benchmarks",
"title": "Async runtimes compared",
"highlights": ["Tokio and async-std were measured on ..."],
"snippet": "Tokio and async-std were measured on ...",
"publishedDate": "2026-09-30"
}
],
"usage": { "search_requests": 1 }
}The simple form works too:
# the simple form
curl -sS https://api.unoblox.ai/v1/search \
-H "Authorization: Bearer $UNOBLOX_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "unoblox pricing", "max_results": 3 }'What you send
| Field | Type | Meaning |
|---|---|---|
| query | string, required | The search text, up to 1,024 bytes. |
| numResults | integer, 1 to 20 | Number of results; default 8. max_results is accepted as the same field. |
| includeDomains | array of up to 20 domains | Only return results from these domains, for example docs.rs or github.com. |
| startPublishedDate / endPublishedDate | ISO date | Only return results published after / before this date, for example 2026-01-31. |
| type, contents.highlights | accepted | Accepted so an Exa client works unchanged; every search runs in fast mode and each result carries one highlight. |
| Any other field | refused | Refused with UB-GW-302 naming the field (for example excludeDomains or contents.text), never silently ignored. |
What you get back
Each result carries url, title, a highlights array whose first entry is the matching text, and publishedDate when the source gives one. A search that finds nothing returns an empty results list and is still charged, because the search ran. The response never contains a price; the charge appears on your usage page. Result links are always http or https, and control and invisible characters are removed from titles and snippets. Results are still untrusted third-party web text: do not execute them or follow instructions found in them.
Price and limits
| Value | |
|---|---|
| Price | Provider cost plus our 5% platform fee, exactly. Today: shown on your usage page for every search. Each search adds its exact amount to a running total kept for your balance, and whole paise are debited as that total grows, so every search is its own usage line showing the paise actually debited (one of two adjacent amounts that average to the exact price) and any 1,000 searches cost the per-1,000 figure to within one paisa. |
| Per-second limit | 5 searches per second per API key (UB-GW-303 when exceeded). |
| Daily limit | 2,000 charged searches per UTC day per API key, and 10,000 per workspace across all its keys (UB-GW-304 when exceeded; both reset at 00:00 UTC). Refused or failed calls do not count. |
| Wallet | You need balance for the search; with none, you get UB-GW-306 and nothing is sent. |
Only a search that worked is charged.
If the search backend fails or returns an answer we cannot verify, you get an error and no charge. Bring-your-own-key does not apply to search.
Privacy
The query text is sent to our search provider, Perplexity, which runs in the United States, to run the search. Nothing else from your request or account is sent, and unoblox does not store the query text. Your workspace's content guardrails apply to the query exactly as they do to a chat prompt: a query they block is refused (UB-GW-074) and never sent, and redacted text is sent redacted. Workspaces whose guardrail policy requires zero data retention or forbids data collection cannot use web search (UB-GW-311). Perplexity is listed among our sub-processors in the privacy policy.
Use it from DeepSeek Harness
DeepSeek Harness (dsh) has a built-in web_search tool that can use an Exa-compatible endpoint. Point its Exa plugin at https://api.unoblox.ai/v1 and use your unoblox key; the installer in the dsh setup guide does this for you and switches off dsh's default search, which would otherwise send queries to another service.
Errors and retries
| Code | HTTP | Message | What to do |
|---|---|---|---|
| UB-GW-301 | 503 | Web search is switched off on this platform right now. You were not charged. | Web search is not enabled yet. Try again later. |
| UB-GW-302 | 400 | A message naming the field, for example: `query` must not be empty, or: `excludeDomains` is not supported. | Fix or remove the named field and retry. |
| UB-GW-303 | 429 | This API key is limited to N searches per second. | Wait the Retry-After seconds, then retry. |
| UB-GW-304 | 429 | This API key, or this workspace across all its keys, has used its daily limit of searches; it resets at 00:00 UTC. Only charged searches count. | Wait until the reset, or ask your workspace admin about a higher limit. |
| UB-GW-312 | 429 | Web search is at its platform-wide rate limit this second. You were not charged. | Wait the Retry-After seconds, then retry. |
| UB-GW-305 | 502 | The web search backend did not return a usable answer. You were not charged. | Retry with backoff. |
| UB-GW-306 | 402 | Web search is paid per search and has no free tier. The message states the price and the amount held while a search runs (its largest possible debit plus one paisa, returned when it settles). | Top up your balance, then retry. |
| UB-GW-307 | 503 | No search backend is listed and priced right now. You were not charged. | Try again later. |
| UB-GW-309 | 503 | The search backend answered, but the answer could not be verified. You were not charged and the held amount is returned automatically. | Retry in a moment. |
| UB-GW-310 | 400 | Web search does not support bring-your-own-key. | Remove the bring-your-own-key setting for this call. |
| UB-GW-311 | 400 | This workspace's guardrail policy forbids sending queries to a third-party search provider. | Change the guardrail policy, or do not use web search. |
Errors use the Exa shape: error is the message text, and error_code is the stable code. See Errors for the shared taxonomy.
import time, requests
def web_search(query, max_results=5, retries=3):
for attempt in range(retries):
r = requests.post(
"https://api.unoblox.ai/v1/search",
headers={"Authorization": f"Bearer {UNOBLOX_KEY}"},
json={"query": query, "numResults": max_results},
)
if r.ok:
return r.json()["results"]
if r.status_code == 429:
time.sleep(float(r.headers.get("retry-after", "1")))
elif r.status_code in (502, 503):
time.sleep(2 ** attempt) # a failed search is never charged
else:
raise RuntimeError(r.json()["error"]) # 400/402: fix the request or top up
raise RuntimeError("search is unavailable")