Python SDK
typesearch — the official client for Python, sync and async.
pip install typesearchPython 3.9+. Built on httpx and pydantic: every response is a typed model, and unknown fields from a
newer API are kept instead of breaking your code.
Create a client
from typesearch import Typesearch
ts = Typesearch() # reads TYPESEARCH_API_KEY| Argument | Default | |
|---|---|---|
api_key | TYPESEARCH_API_KEY | Your key. |
base_url | https://api.typesearch.ai | Or TYPESEARCH_BASE_URL. |
timeout | 70.0 | Seconds before a request is aborted. A deep search can take about a minute. |
max_retries | 2 | Retries on connection errors, 429 rate_limited and 5xx. |
default_headers | — | Headers sent with every request. |
http_client | — | Your own httpx.Client (or httpx.AsyncClient for async), for proxies or tests. |
Use it as a context manager to close connections when you are done: with Typesearch() as ts: ….
Search
res = ts.search(
"el dólar",
mode="normal",
max_results=10,
include_domains=["infobae.com", "lanacion.com.ar"],
published_after="2026-09-20",
highlights=True,
)
for r in res.results:
print(f"{r.score:.2f}", r.title, r.highlights[:1])Keyword arguments have the same names as the HTTP API. Dates accept strings
or datetime.date / datetime.datetime. days=None searches the whole index; leaving days out keeps
the default of 7.
Several queries at once:
res = ts.search(["el dólar", "el FMI"], mode="fast")
for group in res.groups or []:
print(group.query, group.total)Stream
with ts.search_stream("el dólar", mode="deep") as stream:
for event in stream:
if event.type == "step":
print("·", event.step.text)
elif event.type == "partial":
render(event.response.results)
elif event.type == "result":
render(event.response.results)
# Or only the final result
final = ts.search_stream("el dólar").final_response()An error event is raised as an APIError.
Similar and contents
similar = ts.similar("https://www.lanacion.com.ar/economia/…", exclude_domains=["lanacion.com.ar"])
pages = ts.contents(["https://www.infobae.com/economia/…"], query="el dólar")Live site search
# Create the job and wait for it
res = ts.site_search_and_wait("lanacion.com.ar", "el dólar", mode="normal")
# Or handle the job yourself
job = ts.site_search("lanacion.com.ar", "el dólar")
done = ts.jobs.wait(job.id, poll_interval=2, timeout=120)jobs.wait() raises JobFailedError if the job fails.
Async
AsyncTypesearch has the same methods, awaitable:
import asyncio
from typesearch import AsyncTypesearch
async def main():
async with AsyncTypesearch() as ts:
res = await ts.search("el dólar", mode="fast")
async for event in ts.search_stream("el FMI"):
...
asyncio.run(main())Errors
Every error subclasses TypesearchError. API errors are APIError subclasses with status, code,
request_id and, for invalid requests, errors per field.
| Class | When |
|---|---|
BadRequestError | 400 |
AuthenticationError | 401 |
BudgetError | 402 — max_tokens too small |
PermissionDeniedError | 403 |
NotFoundError | 404 |
RateLimitError | 429 — with retry_after |
InternalServerError | 5xx |
APIConnectionError · APITimeoutError | No response, or too slow |
JobFailedError | A live site search job failed |
from typesearch import APIError, BadRequestError, RateLimitError
try:
ts.search("x")
except BadRequestError as e:
print(e.code, e.errors)
except RateLimitError as e:
print(e.code, e.retry_after)
except APIError as e:
print(e.status, e.code, e.request_id)Retries
Connection errors, 429 rate_limited and 5xx are retried with exponential backoff and jitter,
honouring Retry-After; quota_exceeded never is. Set max_retries=0 to handle them yourself.