Web Search
POST /v1/web-search
Search the web and get structured results with titles, URLs, and metadata. General search is served by Exa or Tavily. Set type to academic to search peer-reviewed papers instead, or pin X-Quantized-Provider to choose a provider yourself.
Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <api-key-or-jwt> |
Content-Type |
Yes | application/json |
X-Quantized-Provider |
No | Force a provider (exa, tavily or consensus) |
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | Yes | — | The search query |
num_results |
integer | No | 5 | Number of results to return (1–100) |
type |
string | No | general |
general or academic |
include_text |
boolean | No | false |
Return the page text on each result |
max_characters |
integer | No | 4000 | Cap on text length per result (100–100000) |
Choosing what to search
type says what kind of source you want, so you don’t have to know which provider
serves it. academic searches Consensus,
which indexes peer-reviewed papers.
type |
X-Quantized-Provider |
Searches |
|---|---|---|
omitted or general |
none | The general web (Exa or Tavily) |
academic |
none | Peer-reviewed papers (Consensus) |
academic |
exa |
The general web — an explicit header always wins |
omitted or general |
consensus |
Peer-reviewed papers — the header wins here too |
type only picks a provider when you have not. It never overrides a header you sent,
and it never widens what your license is allowed to reach: if your license type is
pinned to a set of providers that excludes Consensus, type: "academic" returns a
400 rather than falling back to a general provider.
Any value other than general or academic is rejected with a 422 before a
provider is chosen, so no credits are spent.
Getting page content
Set include_text: true when you need something an agent can actually read. Without
it you get metadata only, which is enough to rank or cite a result but not to answer a
question from it.
How much text you get back depends on the provider, because each behaves differently:
| Provider | Default | With include_text: true |
|---|---|---|
| Exa | No text. Exa returns none unless asked. | Full page text, capped |
| Tavily | A relevance snippet, roughly 500–1500 characters | Full page text, capped. Falls back to the snippet when Tavily has no page text |
Consensus (academic) |
The paper’s abstract | The abstract. Consensus has no full-text option |
Either way the text is truncated to max_characters. The default of 4000 keeps a
five-result search near 20KB; raise it if you are feeding a model with a large context
window, but note a single page can exceed 140,000 characters uncapped.
Requesting text does not cost extra. If you need the complete page rather than a
capped extract, use /v1/fetch with the URLs you got back.
Examples
curl -X POST https://api.quantized.us/v1/web-search \
-H "Authorization: Bearer sk-quantized-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Python 3.13 new features",
"num_results": 3
}'
import httpx
response = httpx.post(
"https://api.quantized.us/v1/web-search",
headers={"Authorization": "Bearer sk-quantized-YOUR-KEY"},
json={"query": "Python 3.13 new features", "num_results": 3},
)
data = response.json()
for result in data["results"]:
print(f"{result['title']}: {result['url']}")
Academic search
curl -X POST https://api.quantized.us/v1/web-search \
-H "Authorization: Bearer sk-quantized-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "does caffeine improve memory",
"type": "academic",
"num_results": 3
}'
import httpx
response = httpx.post(
"https://api.quantized.us/v1/web-search",
headers={"Authorization": "Bearer sk-quantized-YOUR-KEY"},
json={
"query": "does caffeine improve memory",
"type": "academic",
"num_results": 3,
},
)
for paper in response.json()["results"]:
print(f"{paper['title']} ({paper['published_date']}) — {paper['id']}")
The response shape is identical to a general search. url is the paper’s page on
consensus.app, id is its DOI when it has one, and text is the abstract:
{
"results": [
{
"title": "How Does Caffeine Influence Memory?",
"url": "https://consensus.app/papers/how-does-caffeine-influence-memory-.../",
"id": "10.31234/osf.io/5rf6x",
"published_date": "2021-05-13",
"author": "Ruochong Zhang, C. Madan",
"text": "Caffeine is a widely used nootropic drug...",
"score": null
}
],
"usage": {
"search_time": null,
"credits_used": 1000000
}
}
Response
{
"results": [
{
"title": "What's New in Python 3.13",
"url": "https://docs.python.org/3/whatsnew/3.13.html",
"id": "r1",
"published_date": "2025-01-15",
"author": "Python Software Foundation",
"text": "Python 3.13 is the latest stable release...",
"score": 0.94
},
{
"title": "Python 3.13 Release Notes",
"url": "https://www.python.org/downloads/release/python-3130/",
"id": "r2",
"published_date": "2024-10-07",
"author": null,
"text": null,
"score": null
}
],
"usage": {
"search_time": 0.42,
"credits_used": 8000
}
}
Response fields
| Field | Type | Description |
|---|---|---|
results |
array | List of search results |
results[].title |
string | Page title |
results[].url |
string | Page URL |
results[].id |
string or null | Result ID (provider-specific — a DOI on academic) |
results[].published_date |
string or null | Publication date. On academic this falls back to the publication year alone when the full date is unknown |
results[].author |
string or null | Author name. On academic, all authors joined with commas |
results[].text |
string or null | Page text, capped at max_characters. On academic, the paper’s abstract |
results[].score |
float or null | Provider relevance score. Always null on academic |
usage.search_time |
float or null | Search duration in seconds. Always null on academic — Consensus does not report one |
usage.credits_used |
integer | Micro-credits consumed |
Providers cap the result count, and an academic query may simply have fewer matches.
Read results.length rather than assuming you got num_results back.
Every field except title and url can be null, and which ones are populated depends
on the provider. Exa supplies published_date and author; Tavily supplies score
and a snippet in text but never published_date or author on a general search;
Consensus supplies the abstract in text but never score. Treat all of them as
optional rather than assuming a provider.
Errors
| Status | Condition |
|---|---|
400 |
Invalid request (missing query), or no provider available for the request — including type: "academic" when your license cannot reach Consensus |
401 |
Invalid or missing API key |
402 |
Insufficient credits |
422 |
Unsupported parameter or invalid field structure, including a type other than general or academic |
503 |
Search provider unavailable, rate limited, or (on academic) another search was already in flight |
A failed search is not billed.