Web search
Search the live web from your own code. Structured results, one flat price per search, no scraping and no search-engine contract.
/v1/searchThis is a plain request/response endpoint, not a chat completion. It returns search results as data — title, URL, snippet, source, publication date, favicon and thumbnail — so you can rank, filter or cite them yourself. Nothing is summarised by a model on the way out, which means nothing is invented on the way out either.
curl https://api.xkiro.com/v1/search \
-H "Authorization: Bearer $XKIRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "xkiro/web-search",
"query": "who won the 2026 Nobel Prize in Physics",
"max_results": 5
}'Request body#
| Field | Type | Description |
|---|---|---|
modelrequired | string | Always xkiro/web-search. Required even though there is one value — your account limits and spend controls are resolved from it. |
queryrequired | string | string[] | What to search for. Pass an array to run up to five related searches in one request. Each query counts as one billed search. |
max_results | integer | How many results to return, 1–20. Default 10. Does not change the price. |
search_domain_filter | string[] | Restrict results to these domains, up to 20. Bare domains work best (reuters.com); a full URL is accepted and trimmed down for you. |
search_recency_filter | string | day, week, month, year or noLimit (default). |
country | string | Two-letter ISO country code, e.g. VN, to bias results toward a region. Any other value is ignored rather than guessed at. |
Field names match Perplexity's search API
search_domain_filter and search_recency_filter are spelled the way you already know them, so moving an existing integration across is a base-URL change rather than a rewrite.
Response#
{
"id": "srch_6db90a15-0bf2-48d2-9bc8-d80830aa8bad",
"model": "xkiro/web-search",
"results": [
{
"title": "2026 Nobel Prize in Physics",
"url": "https://www.nobelprize.org/prizes/physics/2026/summary/",
"snippet": "The Royal Swedish Academy of Sciences has decided to award…",
"publishedDate": "2026-10-06T11:45:00",
"source": "nobelprize.org",
"faviconUrl": "https://…/favicon.png",
"thumbnailUrl": "https://…/preview.jpg"
}
],
"usage": { "searches": 1, "remainingToday": 99 }
}| Field | Type | Description |
|---|---|---|
title | string | Page title, with markup stripped. |
url | string | Canonical URL, after redirects. |
snippet | string | The passage the search engine matched against your query — not the page's meta description, which is usually marketing copy. |
publishedDate | string | null | Publication date when the page states one. null means unknown, which is not the same as undated. |
source | string | Domain of the result, e.g. reuters.com. Useful for grouping and for citations. |
faviconUrl | string | null | Site icon, ready to render in a result list. null when there isn't one — we do not guess a path that would 404. |
thumbnailUrl | string | null | Preview image when the page has one. |
Plan allowances#
Web search is included in every paid plan, with a daily allowance counted per account — not per API key, so extra keys do not buy extra quota. The window rolls continuously over 24 hours rather than resetting at midnight.
| Field | Type | Description |
|---|---|---|
Free | 20 | Then pay-as-you-go at $0.01 each, from your wallet. |
Pro | 100 | 10 requests/minute · 3 at a time. |
Pro Plus | 200 | 15 requests/minute · 3 at a time. |
Max | 300 | 20 requests/minute · 4 at a time. |
Ultra | 1,500 | 30 requests/minute · 6 at a time. |
Power | 3,000 | 60 requests/minute · 8 at a time. |
Search and page reads have separate allowances
A heavy day of searching never eats into your page-read allowance, or the other way round. Each response carries usage.remainingToday so you always know where you stand without having to hit the limit first.
Pricing#
- On a paid plan, searches inside your allowance cost nothing. When the allowance runs out you get a
429telling you how long until it frees up — your wallet is never charged behind your back for something the plan said was included. - Without a plan: 20 free searches a day, then $0.01 each — $10 per 1,000. One request with one query is one search; a request with three queries is three.
- The content is included either way. You are not billed tokens for the pages we read on your behalf, so the cost of a search does not depend on how long the results turn out to be.
- A failed search is not billed. If no search ran, nothing is charged.
Errors#
| Field | Type | Description |
|---|---|---|
400 | invalid_request | Missing or malformed field. The message names the field. |
401 | authentication_error | Missing or invalid API key. |
402 | insufficient_quota | Free allowance used up and wallet balance is zero. |
429 | rate_limit | Your plan's daily allowance is used up, or you exceeded the per-minute or concurrent limit. The message says which, and how long to wait. |
502 | no_search_performed | No search ran for this query — rephrase it. You are not billed for this. |
503 | service_unavailable | Temporarily unavailable. Retry shortly. |
Practical notes
- Write queries the way you would type them into a search box, not the way you would ask a chatbot. Keywords beat full sentences.
- Use
search_domain_filterwhen you already know the sources you trust. It is the single biggest lever on result quality. - Cache results by query. Yesterday’s answer to a question about last year is still today’s answer, and it costs nothing to reuse.
- Feeding results to a model? Pass
title,urlandsnippetand ask it to cite the URL. To read a full page, call web fetch.
