Official Python SDK for Octen API - Web Search, URL Extract, Text/VL Embeddings, and LLM Chat
Project description
Octen Python SDK
Official Python SDK for the Octen API — web search, URL extraction, text embeddings, and multi-model LLM chat in one package.
✨ Features
- 🔍 Web Search — search and retrieve ranked web results with filtering, highlighting, and full content
- 🌐 URL Extract — fetch and parse 1-20 URLs in a single batch with markdown / text output, query-driven highlights, and media (images / videos / audio / favicon)
- 💬 Multi-model Chat — access 15+ LLMs (GPT, Claude, Gemini, Kimi, MiniMax, Qwen, DeepSeek) through a single unified API
- 🧮 Text Embeddings — convert text into high-quality vector representations
- 🖼️ VL (Multimodal) Embeddings — encode text, images, and videos into a single fused vector or independent per-element vectors
- ⚡ Streaming (SSE) — real-time token streaming with typed event objects
- 🔄 Auto Retry — exponential backoff for transient errors
- 🛡️ Type Safe — full Pydantic models with IDE auto-completion
- 🔀 Async Support — native
asyncioclient for concurrent workloads - 📦 HTTP/2 — connection pooling and keep-alive out of the box
📦 Installation
pip install octen
Requires Python 3.8 or higher.
Development Version
pip install octen[dev]
Async Support
pip install octen[async]
🚀 Quick Start
Search
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.search.search(query="Python programming", count=5)
for result in response.results:
print(f"Title: {result['title']}")
print(f"URL: {result['url']}")
print(f"Highlight: {result.get('highlight', '')}")
Broad Search
Decompose a query into multiple sub-queries, search them concurrently, and get results grouped per sub-query. Best for comparisons (pricing, products, vendors), surveys/research, and multi-angle questions where a single search() only reaches a few subtopics — pass the query as-is and raise max_queries for broader coverage.
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.search.broad_search(
query="compare cloud GPU pricing across providers",
max_queries=5, # up to N sub-queries (1-30, default 5)
count=10, # per-sub-query SearchOptions, same as search()
)
print("Sub-queries:", response.queries)
for group in response.search_results:
print(f"\n# {group['query']} ({group.get('latency')} ms)")
for result in group["results"]:
print(f" {result['title']} — {result['url']}")
Extract
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.extract.extract(
urls=["https://example.com", "https://octen.ai"],
format="markdown",
)
for item in response.items:
if item.status == "success":
print(f"{item.title} — {item.url}")
else:
print(f"FAILED {item.url}: {item.error_message}")
Chat
from octen import Octen, ChatMessage, OctenSearchTool
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Hello!")],
tools=[OctenSearchTool()], # enable live web search
)
print(response.text)
Embeddings
from octen import Octen
with Octen(api_key="your-api-key") as client:
embedding = client.embedding.create(
input=["Hello, world!"],
model="octen-embedding-4b"
)
vector = embedding.get_first_embedding()
print(f"Vector dimension: {len(vector)}")
VL (Multimodal) Embeddings
from octen import Octen
with Octen(api_key="your-api-key") as client:
# Fuse text + image into one vector
response = client.vl_embedding.create(
model="octen-vl-embedding-large",
contents=[
{"text": "A cute orange cat on a wooden chair"},
{"image": "https://example.com/cat.jpg"},
],
enable_fusion=True,
)
vector = response.get_first_embedding()
if vector is not None:
print(f"Fused vector dimension: {len(vector)}")
🔍 Search API
Advanced Search
from octen import Octen, HighlightOptions, FullContentOptions
with Octen(api_key="your-api-key") as client:
response = client.search.search(
query="machine learning best practices",
count=10,
topic="news", # general (default) or news
country="US", # ISO 3166-1 alpha-2 country code, or "auto" (default)
include_domains=["github.com", "arxiv.org"], # Search only these domains
time_range="month", # Relative time window
include_images=True, # Return image URLs per result
highlight=HighlightOptions(
enable=True,
max_tokens=500
),
full_content=FullContentOptions(
enable=True,
max_tokens=2000
),
timeout=60.0 # Custom timeout
)
print(f"Found {len(response.results)} results")
print(f"Token usage: {response.usage}")
🌐 Extract API
Fetch and parse one or more URLs into structured content. A single request accepts 1-20 URLs and is served as one upstream batch.
Batch Extract
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.extract.extract(
urls=[
"https://example.com",
"https://octen.ai",
"https://www.iana.org/about",
],
format="markdown", # "text" or "markdown" (default markdown)
max_age_seconds=600, # accept results cached within 10 minutes
timeout=30, # per-URL upstream fetch budget (1-60s)
include_images=True, # also returns cover_image when the page has one
request_timeout=90.0, # local httpx deadline; >= timeout + overhead
)
print(f"OK: {response.successful_urls}/{response.total_urls} ({response.latency}ms)")
for item in response.items:
if item.status == "success":
print(f" ✓ {item.title} — {item.url}")
if item.favicon: # favicon is returned by default when available
print(f" favicon: {item.favicon}")
if item.cover_image:
print(f" cover: {item.cover_image.url}")
else:
# Partial-success is first-class: failed URLs don't poison siblings.
print(f" ✗ {item.url}: {item.error_message}")
Note:
response.itemsis parsed lazily and raisesOctenAPIErrorif any row fails to parse (signals server schema drift). The raw dicts remain accessible viaresponse.resultsas an escape hatch.
Query-driven Highlights
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.extract.extract(
urls=["https://en.wikipedia.org/wiki/Python_(programming_language)"],
query="async programming", # max 500 chars
)
for item in response.items:
for snippet in item.highlights or []:
print(f"• {snippet}")
Single URL (Convenience Shortcut)
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.extract.simple_extract("https://example.com")
print(response.items[0].title)
Two Timeouts, Two Layers
timeout and request_timeout operate at different layers — easy to confuse, important to get right:
| Parameter | Layer | Controls | On timeout |
|---|---|---|---|
timeout (int, 1-60s) |
server | upstream per-URL fetch budget | the slow URL is reported with status="failed" and an error_message; sibling URLs in the same batch are returned normally as long as the upstream responds within the bounded round trip |
request_timeout (float) |
client (httpx) | local socket deadline for the whole HTTP call | raises OctenTimeoutError |
Rule of thumb: request_timeout >= timeout + network_overhead.
🖼️ Image Search API
In Beta — contact us to request beta access.
Multimodal image search by text query and/or a reference image URL.
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.image_search.search(
query="golden retriever puppy", # text input
# image_url="https://example.com/ref.jpg", # optional reference image
topic="general", # "general" or "design"
count=5, # 1-10 (default 5)
)
for result in response.results:
print(result["title"], result["url"], result.get("thumbnail"))
🎬 Video Search API
In Beta — contact us to request beta access.
Text-driven video search.
from octen import Octen
with Octen(api_key="your-api-key") as client:
response = client.video_search.search(
query="how to make fresh pasta",
count=5, # 1-10 (default 5)
)
for result in response.results:
print(result["title"], result["url"], result.get("duration_seconds"))
💬 Chat API
Web search is now a tool. The old top-level
web_search/web_search_optionsparameters were removed. Enable web search by passing anOctenSearchToolintools; tune it withOctenSearchToolParameters. For broad (multi-query) search, pass anOctenBroadSearchToolwithOctenBroadSearchToolParametersinstead.
Non-streaming
from octen import Octen, ChatMessage, OctenSearchTool, OctenSearchToolParameters
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="openai/gpt-5.4",
messages=[
ChatMessage(role="system", content="You are a helpful assistant."),
ChatMessage(role="user", content="What happened in tech today?"),
],
tools=[OctenSearchTool(parameters=OctenSearchToolParameters(safesearch="off", count=5))],
max_tokens=500,
temperature=0.7
)
print(response.text)
print(f"Tokens used: {response.usage.total_tokens}")
# Access search results
if response.search_results:
for group in response.search_results:
for item in group.results:
print(f" - {item.title}: {item.url}")
Streaming
from octen import Octen, ChatMessage, OctenSearchTool
with Octen(api_key="your-api-key") as client:
for event in client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Tell me a story")],
stream=True,
tools=[OctenSearchTool()],
):
if event.type == "search_done":
print(f"[{len(event.search_results or [])} search groups]")
elif event.type == "content" and event.choices:
print(event.choices[0].delta.content or "", end="", flush=True)
elif event.type == "finish":
print() # newline
elif event.type == "usage" and event.usage:
print(f"[total tokens: {event.usage.total_tokens}]")
Tool Calling
from octen import Octen
from octen.models import ChatMessage, Tool, ToolFunction
weather_tool = Tool(
function=ToolFunction(
name="get_weather",
description="Get current weather for a city",
parameters={
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
},
"required": ["city"],
}
)
)
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="What's the weather in London?")],
tools=[weather_tool],
tool_choice="auto"
)
if response.choices[0].finish_reason == "tool_calls":
tc = response.choices[0].message.tool_calls[0]
print(f"Tool: {tc.function.name}, Args: {tc.function.arguments}")
JSON Output Mode
from octen import Octen, ChatMessage
from octen.models import ResponseFormat
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="google/gemini-3-flash-preview",
messages=[ChatMessage(role="user", content="Return a JSON list of 3 programming languages")],
response_format=ResponseFormat(type="json_object"),
)
print(response.text)
Web Search with Full Page Content
from octen import Octen, ChatMessage, OctenSearchTool, OctenSearchToolParameters
from octen.models.search import FullContentOptions
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Latest Python 3.13 features?")],
tools=[OctenSearchTool(parameters=OctenSearchToolParameters(
safesearch="off",
full_content=FullContentOptions(enable=True, max_tokens=2048),
))],
)
print(f"Full content tokens: {response.usage.full_content_tokens}")
Broad Web Search (multi-query)
The octen_broad_search tool fans the question out into up to max_queries
sub-queries and searches them concurrently — a peer of octen_search in
tools. Results land in response.search_results as usual.
from octen import Octen, ChatMessage, OctenBroadSearchTool, OctenBroadSearchToolParameters
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Compare the leading vector databases")],
tools=[OctenBroadSearchTool(parameters=OctenBroadSearchToolParameters(
max_queries=3, # up to 3 generated sub-queries (1-30, default 5)
count=5, # results per sub-query
))],
)
print(response.text)
for group in response.search_results or []:
print(f"[{group.query}] {len(group.results)} results")
Prompt Caching (content blocks)
from octen import Octen, ChatMessage, ChatContentBlock, CacheControl
with Octen(api_key="your-api-key") as client:
response = client.chat.create(
model="anthropic/claude-sonnet-4.6",
messages=[
ChatMessage(role="user", content=[
ChatContentBlock(
type="text",
text="<long reusable context...>",
cache_control=CacheControl(ttl="1h"), # 5m (default) or 1h
),
ChatContentBlock(type="text", text="Summarise the above."),
]),
],
)
print(response.usage.prompt_tokens_details.cached_tokens)
🤖 Supported Chat Models
For the full and up-to-date list of supported models, visit the Octen official website.
🧮 Embeddings API
Batch Embeddings
from octen import Octen
with Octen(api_key="your-api-key") as client:
# Process multiple texts
texts = [
"Artificial intelligence is transforming the world",
"Applications of deep learning",
"Natural language processing technology"
]
response = client.embedding.create(
input=texts,
model="octen-embedding-8b",
input_type="document"
)
vectors = response.get_embeddings()
print(f"Generated {len(vectors)} vectors")
# Or use convenience methods
query_vector = client.embedding.embed_query("search query")
doc_vectors = client.embedding.embed_documents(["document 1", "document 2"])
Multimodal (VL) Embeddings
The vl_embedding resource encodes text, images, and videos through the
/vl-embedding endpoint. Two output modes:
- Fusion (
enable_fusion=True) — all contents collapse into a single vector (type="fusion") - Independent (
enable_fusionomitted orFalse) — one vector per content element (type="vl"). Omitting the field inherits the server default (False).
Default-value semantics:
enable_fusion/fps/instructdefault toNonein the SDK and are stripped from the wire viaexclude_none=True. When omitted, the server applies its own defaults (shown in the table).enable_fusion=Falseand omitting the field produce different wire payloads but currently the same behavior — passFalseexplicitly only if you need to override a future server default change.
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | ✓ | octen-vl-embedding (max dim 2048) or octen-vl-embedding-large (max dim 4096) |
contents |
list[dict | VLEmbeddingContent] | ✓ | Each item has exactly one of text / image / video (non-empty). Max 20 total, 5 images, 1 video |
enable_fusion |
bool | — | Single fused vector vs. independent vectors. Omit → server default False |
dimension |
int | — | Output dim, ≤ model's max. Omit → model's max |
fps |
float (0-1) | — | Video frame sampling density. Omit → server default 1; lower values reduce video token cost |
instruct |
string | — | Custom task description. Omit → server default "Represent the user's input." |
Content element constraints:
text— string, ≤ 32,000 tokens per entryimage— URL or Base64 string, ≤ 5MB, formats: JPEG/PNG/WEBP/BMP/TIFF/ICO/DIB/ICNS/SGIvideo— URL only, ≤ 50MB, formats: MP4/AVI/MOV
from octen import Octen, VLEmbeddingContent
with Octen(api_key="your-api-key") as client:
# 1) Text-only embedding
response = client.vl_embedding.create(
model="octen-vl-embedding",
contents=[{"text": "What is multimodal vector search?"}],
)
vector = response.get_first_embedding()
if vector is not None:
print(f"text-only dim={len(vector)}") # 2048
# 2) Fused multimodal embedding (text + images + video)
response = client.vl_embedding.create(
model="octen-vl-embedding-large",
contents=[
{"text": "Outdoor tent, 3-4 person, waterproof"},
{"image": "https://example.com/tent_setup.jpg"},
{"image": "https://example.com/tent_inside.jpg"},
{"video": "https://example.com/tent_demo.mp4"},
],
enable_fusion=True,
dimension=2048,
fps=0.3,
instruct="Represent the outdoor product for retrieval",
)
print(f"fusion type={response.items[0].type} dim={response.items[0].dimension}")
# 3) Independent per-image embeddings
response = client.vl_embedding.create(
model="octen-vl-embedding",
contents=[
{"image": "https://example.com/product_1.jpg"},
{"image": "https://example.com/product_2.jpg"},
],
enable_fusion=False,
)
for item in response.items:
print(f"index={item.index} type={item.type} dim={item.dimension}")
# 4) Typed content objects (alternative to dicts)
response = client.vl_embedding.create(
model="octen-vl-embedding",
contents=[
VLEmbeddingContent(text="A photo of a cat"),
VLEmbeddingContent(image="https://example.com/cat.jpg"),
],
enable_fusion=True,
)
# Usage breakdown
if response.usage:
print(
f"input_tokens={response.usage.input_tokens} "
f"text_tokens={response.usage.text_tokens} "
f"image_tokens={response.usage.image_tokens} "
f"image_count={response.usage.image_count} "
f"duration={response.usage.duration}s"
)
Async usage:
import asyncio
from octen import AsyncOcten
async def main():
async with AsyncOcten(api_key="your-api-key") as client:
response = await client.vl_embedding.create(
model="octen-vl-embedding-large",
contents=[
{"text": "modern art exhibition"},
{"image": "https://example.com/painting.jpg"},
],
enable_fusion=True,
)
print(response.get_first_embedding()[:3])
asyncio.run(main())
📖 Full API reference: https://docs.octen.ai/api-reference/vl-embedding
Custom Configuration
from octen import Octen
client = Octen(
api_key="your-api-key",
base_url="https://api.octen.ai", # Custom API endpoint
timeout=10.0, # Global default timeout (seconds)
max_retries=3, # Maximum retry attempts
http2=True # Enable HTTP/2
)
try:
# This request uses global timeout (10 seconds)
response1 = client.search.search("query 1")
# This request overrides timeout to 30 seconds
response2 = client.search.search("complex query", timeout=30.0)
finally:
client.close() # Release connection pool resources
📚 API Documentation
Search API
client.search.search()
Perform a web search query.
Parameters:
query(str, required): Search query string, max 500 characterstopic(str, optional): Search category, options:"general"(default),"news"country(str, optional): Country code for region-specific search results — ISO 3166-1 alpha-2 (e.g."US","JP"), or"auto"(default) to determine automaticallycount(int, optional): Number of results to return, range 1-100, default 5include_domains(List[str], optional): Include only results from these domains (max 1000, each ≤30 chars)exclude_domains(List[str], optional): Exclude results from these domains (max 150, each ≤30 chars)include_text(List[str], optional): Results must contain these texts (max 5, each ≤30 chars)exclude_text(List[str], optional): Results must exclude these texts (max 5, each ≤30 chars)time_basis(str, optional): Time basis, options:"auto","published","crawled"time_range(str, optional): Relative time window, options:"day","week","month","year"(or"d","w","m","y")start_time(str, optional): Start time in ISO 8601 formatend_time(str, optional): End time in ISO 8601 formathighlight(HighlightOptions, optional): Highlight options (max_tokens100–20000, default 512)format(str, optional): Content format, options:"text","markdown"safesearch(str, optional): Safe search, options:"off","strict"(default)full_content(FullContentOptions, optional): Full content options (max_tokens100–100000, default 2048)include_images(bool, optional): Include image URLs in results, defaultfalseinclude_videos(bool, optional): Include video URLs in results, defaultfalsetimeout(float, optional): Request timeout in seconds
Returns: SearchResponse object
Response Properties:
results- List of search resultsquery- The actual query usedusage- Token usage informationlatency- Latency information
client.search.broad_search()
Decompose a query into related sub-queries, search them concurrently, and return results grouped per sub-query.
Parameters:
query(str, required): Original search query, max 500 charactersmax_queries(int, optional): Upper bound on sub-queries generated, range 1-30, default 5- all per-sub-query options from
search()(topic,country,count,include_domains,exclude_domains,include_text,exclude_text,time_basis,time_range,start_time,end_time,highlight,format,safesearch,full_content,include_images,include_videos) — applied to every sub-query (sent nested insidesearch_options) timeout(float, optional): Request timeout in seconds
Returns: BroadSearchResponse object
Response Properties:
queries- List of generated sub-queriessearch_results- Per-sub-query result groups ({query, results, latency})query- The original queryusage- Token usage informationlatency- Latency information
Extract API
client.extract.extract()
Fetch and parse one or more URLs in a single batch.
Parameters:
urls(List[str], required): URLs to extract — 1-20 per request, each ≤ 2048 charactersformat(str, optional): Content format —"text"or"markdown"(server default"markdown")max_age_seconds(int, optional): Cache window in seconds (minimum300); older cached versions are re-fetched. Defaults to86_400(24h) when omitted. Values outside the allowed range are adjusted server-sidequery(str, optional): Query for per-result highlight extraction, max 500 characters. When set, each item'shighlightsfield is populatedtimeout(int, optional): Per-URL upstream fetch budget in seconds, range 1-60, default 30. A URL that exceeds this returnsstatus="failed"— siblings continueinclude_images(bool, optional): Return image objects, andcover_imagewhen the page has one (default False)include_videos(bool, optional): Return video objects (default False)include_audio(bool, optional): Return audio objects (default False)request_timeout(float, optional): Local HTTP socket deadline (httpx), distinct from the upstreamtimeoutabove. Set>= timeout + network_overhead
Caller typos (e.g.
url=instead ofurls=,inculde_images=) are rejected at construction time by Pydantic — they will not silently reach the server.
Returns: ExtractResponse object
Response Properties:
code(int) —0on successmsg(str) — Server messagerequest_id(str | None) — Server-generated request id; echoed fromX-Request-Idif you supplied that headerresults(List[dict]) — Raw per-URL result dictsitems(List[ExtractItem]) — Parsed, typed per-URL resultsusage(dict | None) —{"total_urls": int, "successful_urls": int}total_urls(int | None) — Convenience accessorsuccessful_urls(int | None) — Number of URLs withstatus="success"in this batchlatency(int | None) — End-to-end server latency in mswarning(str | None) — Non-fatal warning
ExtractItem Fields:
url(str) — The requested URLstatus(Literal["success", "failed"]) — Extraction outcometitle(str | None) — Page titlefull_content(str | None) — Extracted page contenthighlights(List[str] | None) — Snippets whenquerywas settime_published/time_last_crawled(str | None) — ISO 8601 timestampserror_message(str | None) — Populated only whenstatus="failed"favicon(str | None) — Page favicon URL; returned by default when availablecover_image(ExtractCoverImage | None) — Page cover image{url}(wheninclude_images=Trueand the page has one)images/videos/audio(List[Any] | None) — Passthrough media objects; schema is upstream-definedcategory(ExtractCategory | None) —primary/secondaryclassification labels (when classifier enabled server-side)page_structure(ExtractPageStructure | None) —primary/secondarystructure labels
client.extract.simple_extract(url)
Convenience shortcut for a single URL with default parameters.
response = client.extract.simple_extract("https://example.com")
Chat API
client.chat.create()
Create a chat completion (non-streaming or streaming).
Parameters:
messages(List[ChatMessage | dict], required): Conversation history. Each item can be aChatMessageobject or a plain dict{"role": ..., "content": ...}model(str, required): Model ID (e.g."openai/gpt-5.4"). See Supported Chat Models for the full liststream(bool, optional): IfTrue, return aStreamiterator ofStreamEventobjects. DefaultFalsemax_tokens(int, optional): Maximum number of output tokensmax_completion_tokens(int, optional): Alternative max-token parametertemperature(float, optional): Sampling temperature[0, 2]top_p(float, optional): Nucleus sampling probability(0, 1]top_k(int, optional): Top-k samplingmin_p(float, optional): Minimum probability cutoff[0, 1]top_a(float, optional): Top-a sampling[0, 1]repetition_penalty(float, optional): Repetition penalty(0, 2]frequency_penalty(float, optional): Frequency penalty[-2, 2]presence_penalty(float, optional): Presence penalty[-2, 2]response_format(ResponseFormat, optional): Output format —ResponseFormat(type="text"),ResponseFormat(type="json_object"), orResponseFormat(type="json_schema", json_schema=...)stop(List[str], optional): Up to 4 stop sequencesseed(int, optional): Integer seed for deterministic samplingreasoning_effort(str, optional): Chain-of-thought effort:"xhigh","high","medium","low","minimal", or"none"reasoning_max_tokens(int, optional): Max reasoning token budget (min 1024)verbosity(str, optional): Output verbosity:"low","medium", or"high"parallel_tool_calls(bool, optional): Allow parallel tool calls (defaultTrueserver-side)previous_response_id(str, optional): Continue from a previous responselogprobs(bool, optional): Whether to return log probabilitiestop_logprobs(int, optional): Number of most-likely tokens[0, 20]. Requireslogprobs=Truelogit_bias(Dict[str, float], optional): Token ID to bias value mappingtools(List[Tool | OctenSearchTool | OctenBroadSearchTool | dict], optional): Tool definitions available to the model. Three kinds:- Function tool —
Tool(function=ToolFunction(name=..., description=..., parameters=..., strict=...)) - Built-in web search —
OctenSearchTool(parameters=OctenSearchToolParameters(...)). Parameters:max_searches(default 5) plus the shared web-search options:topic(general/news),country(ISO 3166-1 alpha-2, e.g."US"/"JP", or"auto"),count(1-100),include_domains/exclude_domains,include_text/exclude_text(≤5 each),time_basis(auto/published/crawled),time_range(day/week/month/yearord/w/m/y),start_time/end_time(ISO 8601),highlight(HighlightOptions),format(markdown/text),safesearch(off/strict),full_content(FullContentOptions),include_images(bool) - Built-in broad web search —
OctenBroadSearchTool(parameters=OctenBroadSearchToolParameters(...)). Fans out into multiple sub-queries. Parameters:max_queries(1-30, default 5) plus the same shared web-search options asocten_search
- Function tool —
tool_choice(str | dict, optional):"none","auto","required", or a dict specifying a particular tool (e.g.{"type": "octen_search"}or{"type": "function", "function": {"name": ...}})user(str, optional): Opaque end-user identifiertimeout(float, optional): Per-request timeout in seconds (default 60s for chat)
Returns:
ChatCompletionwhenstream=FalseStream(iterable ofStreamEvent) whenstream=True
ChatCompletion Properties:
id- Unique completion IDmodel- Model used for generationchoices- List ofChoiceobjects. Eachmessageexposescontent,reasoning,reasoning_details(List[ReasoningDetail]),annotations(List[Annotation] withurl_citation),tool_calls, andrefusaltext- Convenience accessor for the first choice's contentreasoning- Convenience accessor for the first choice's reasoning (falls back to legacyreasoning_content)usage-Usageobject:prompt_tokens,completion_tokens,total_tokens,num_search_queries,full_content_tokens,prompt_tokens_details(cached_tokens,cache_write_tokens),completion_tokens_details(reasoning_tokens)search_results- List ofChatSearchResult(when theocten_searchtool ran)citations- Citation string referencing search resultswarning- Optional warning message
StreamEvent Properties:
type- Event type:"search_done","content","finish","usage","error"choices- List ofStreamChoice(withdelta.contentanddelta.reasoningfor incremental text)search_results- Web search results (onsearch_doneevent)usage- Token usage (onusageevent)citations- Citation string (onsearch_doneevent)error-StreamErrorwithmessageandcode(onerrorevent)
Embedding API
client.embedding.create()
Create text embedding vectors.
Parameters:
input(str | List[str], required): Input text or list of textsmodel(str, optional): Model name, options:"octen-embedding-0.6b"- Lightweight model"octen-embedding-4b"- Balanced performance"octen-embedding-8b"- Highest quality
dimension(int, optional): Vector dimensioninput_type(str, optional): Input type, options:"query"or"document"truncation(bool, optional): Whether to truncate long inputs, default Truetimeout(float, optional): Request timeout in seconds
Returns: EmbeddingResponse object
Response Methods:
get_embeddings()- Get all vectorsget_first_embedding()- Get first vector (for single input)
Convenience Methods:
embed_query(text)- Embed a single query textembed_documents(texts)- Batch embed document texts
VL Embedding API
client.vl_embedding.create()
Create multimodal embedding vectors from text, images, and videos.
Parameters:
-
model(str, required): Model name, options:"octen-vl-embedding"- Max dimension 2048"octen-vl-embedding-large"- Max dimension 4096
-
contents(List[dict | VLEmbeddingContent], required): Content elements. Each item must set exactly one of:text(str) - Text input, ≤ 32k tokensimage(str) - Image URL or Base64, ≤ 5MB (JPEG/PNG/WEBP/BMP/TIFF/ICO/DIB/ICNS/SGI)video(str) - Video URL, ≤ 50MB (MP4/AVI/MOV)
Limits: ≤ 20 total elements, ≤ 5 images, ≤ 1 video per request.
-
enable_fusion(bool, optional): Fuse all contents into a single vector. Omit → server defaultFalse(independent vectors). The SDK usesexclude_none=True, so omitting and passingFalseproduce different wire payloads -
dimension(int, optional): Output dimension, ≤ model max. Omit → model max -
fps(float, optional): Video frame sampling density, 0 ≤ x ≤ 1. Omit → server default1 -
instruct(str, optional): Custom task description for the encoder. Omit → server default"Represent the user's input." -
timeout(float, optional): Request timeout in seconds
Returns: VLEmbeddingResponse object
Response Properties:
code(int) - Response code;0means success. Non-zero responses raiseOctenAPIErrorat parse timemsg(str) - Server-provided result messagerequest_id- Server-assigned request id (use this to grep server logs)items- List ofVLEmbeddingResultwith.index,.embedding,.type("vl"or"fusion"),.dimension. RaisesOctenAPIErroron schema driftresults- Raw result dicts (escape hatch whenitemsraises)model- Server-reported model usedusage-VLEmbeddingUsagewithinput_tokens/text_tokens/image_tokens(covers sampled video frames too) /image_count(excludes video frames) /duration(video seconds)warning- Optional server warning (string only; non-string payloads returnNone)
Response Methods:
get_embeddings()- Get all vectorsget_first_embedding()- Get first vector (useful in fusion mode)
🔧 Async Support
import asyncio
from octen import AsyncOcten, ChatMessage
async def main():
async with AsyncOcten(api_key="your-api-key") as client:
# Concurrent chat requests
task1 = client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Explain deep learning")],
)
task2 = client.chat.create(
model="anthropic/claude-sonnet-4.6",
messages=[ChatMessage(role="user", content="Explain reinforcement learning")],
)
r1, r2 = await asyncio.gather(task1, task2)
print(r1.text)
print(r2.text)
# Async streaming
stream = await client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Hello!")],
stream=True
)
async for event in stream:
if event.type == "content" and event.choices:
print(event.choices[0].delta.content or "", end="", flush=True)
# Search, extract, and embeddings also work async
results = await client.search.search(query="AI")
extracted = await client.extract.extract(urls=["https://example.com"])
embedding = await client.embedding.create(input=["Hello"], model="octen-embedding-4b")
asyncio.run(main())
⚠️ Error Handling
from octen import (
Octen,
ChatMessage,
OctenAPIError,
OctenTimeoutError,
OctenConnectionError,
OctenRateLimitError,
OctenAuthenticationError,
OctenStreamError,
)
with Octen(api_key="your-api-key") as client:
try:
response = client.chat.create(
model="openai/gpt-5.4",
messages=[ChatMessage(role="user", content="Hello")]
)
except OctenAuthenticationError:
print("Invalid or missing API key")
except OctenRateLimitError as e:
print(f"Rate limited — retry after {e.retry_after}s")
except OctenStreamError as e:
print(f"Stream error: {e.message} (code {e.code})")
except OctenTimeoutError as e:
print(f"Request timed out after {e.timeout}s")
except OctenAPIError as e:
print(f"API error {e.status_code}: {e.message}")
🧪 Development
Install Development Dependencies
# Install development version from source
pip install -e ".[dev]"
Run Tests
pytest tests/
Code Formatting
black octen/
ruff check octen/ --fix
Type Checking
mypy octen/
📝 License
MIT License - See LICENSE file for details
🔗 Links
📧 Support
For questions or help, please:
- Check the Documentation
- Email us at support@octen.ai
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file octen-0.7.2.tar.gz.
File metadata
- Download URL: octen-0.7.2.tar.gz
- Upload date:
- Size: 108.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ffebef8d1c4e7a70f526f8dd0d7e59c8ac308b652b4d43d2a03522ab3bb1de7
|
|
| MD5 |
6ac4f3f6d76727e6b392ccbc80e02f3f
|
|
| BLAKE2b-256 |
9dcb4b11445115aac3bdd9704ff1f44eda6451ade7d0c152fc86fbbe2be0235b
|
File details
Details for the file octen-0.7.2-py3-none-any.whl.
File metadata
- Download URL: octen-0.7.2-py3-none-any.whl
- Upload date:
- Size: 73.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d350919cc1d519fca2ca0998e22e1bcdc6fd4646af8c3454f5554b723356bc8
|
|
| MD5 |
e9a8af99e1220a3c0408818b44747248
|
|
| BLAKE2b-256 |
dbb82f33995c63d439abdcfb574bd1aa9cf332bfe6d475c50fafc3e220671eca
|