Async Python SDK to discover, inspect, call, and audit 10,000+ real-world API capabilities from your own agents and applications.
The SDK gives you two levels of control:
QverisClient — a thin typed wrapper over the QVeris REST API (discover, inspect, call, usage, ledger).Agent — a ready-made LLM tool loop that lets a model discover and call capabilities on its own.Use the client when you want full control, or the agent when you want a working assistant in a few lines.
pip install qveris
Requires Python 3.8+. Runtime dependencies: httpx, pydantic, pydantic-settings, openai.
The SDK reads your API key from the QVERIS_API_KEY environment variable:
export QVERIS_API_KEY="sk-..."
Get a key from the Dashboard / API Keys page. You can also pass configuration explicitly:
from qveris import QverisClient, QverisConfig
client = QverisClient(QverisConfig(api_key="sk-..."))
The default API base URL is https://qveris.ai/api/v1/. Override it with the QVERIS_BASE_URL environment variable or QverisConfig(base_url=...) when targeting a custom endpoint.
The core workflow is discover → inspect → call, then optionally audit what happened. All methods are async.
import asyncio
from qveris import QverisClient
async def main():
client = QverisClient()
try:
# 1. Discover capabilities with natural language (free)
discovered = await client.discover("weather forecast API", limit=5)
tool = discovered.results[0]
# 2. Inspect the selected capability for full parameters
inspected = await client.inspect([tool.tool_id], search_id=discovered.search_id)
selected = inspected.results[0]
# 3. Call it (may consume credits)
params = (
selected.examples.sample_parameters
if selected.examples and selected.examples.sample_parameters
else {"city": "London"}
)
result = await client.call(
selected.tool_id,
params,
search_id=discovered.search_id,
max_response_size=20480,
)
print(result.success, result.result)
# 4. Audit the final charge outcome
usage = await client.usage(execution_id=result.execution_id, summary=True)
ledger = await client.ledger(summary=True, limit=5)
print(usage.total, ledger.total)
finally:
await client.close()
asyncio.run(main())
QverisClientowns an HTTP connection pool. Alwaysawait client.close()when you are done (e.g. in afinallyblock).
Agent wraps the same workflow into an LLM tool loop. The model is given the discover, inspect, and call tools and decides when to use them.
The default agent uses an OpenAI-compatible provider, so set:
export OPENAI_API_KEY="sk-..."
export OPENAI_BASE_URL="https://api.openai.com/v1" # optional; for OpenAI-compatible providers
import asyncio
from qveris import Agent, Message
async def main():
async with Agent() as agent:
messages = [Message(role="user", content="Check the current weather in New York.")]
async for event in agent.run(messages):
if event.type == "content" and event.content:
print(event.content, end="", flush=True)
asyncio.run(main())
Agent is an async context manager — async with Agent() as agent: closes network resources automatically.
When you just want the finished answer:
async with Agent() as agent:
answer = await agent.run_to_completion(
[Message(role="user", content="Find a stock quote capability and quote AAPL.")]
)
print(answer)
Agent.run(messages) yields StreamEvent objects. Inspect event.type:
type | Meaning |
|---|---|
content | Assistant text (delta chunks when streaming, full message otherwise) |
reasoning / reasoning_details | Optional reasoning tokens / structured reasoning from some providers |
tool_call | The model is invoking discover / inspect / call (or one of your extra tools) |
tool_result | Output of an executed tool call (event.tool_result has name, result, is_error) |
metrics | Token usage / timing, when the provider reports it |
error | Fatal error that ended the run |
Pass stream=False to run(...) to receive complete assistant turns instead of deltas.
QverisConfig| Field | Env var | Default | Description |
|---|---|---|---|
api_key | QVERIS_API_KEY | None | API key, sent as Authorization: Bearer ... |
base_url | QVERIS_BASE_URL | https://qveris.ai/api/v1/ | API base URL |
enable_history_pruning | — | True | Prune/compress old tool outputs to save tokens (agent loop) |
max_iterations | — | 50 | Max agent tool-loop iterations |
AgentConfig| Field | Default | Description |
|---|---|---|
model | gpt-4o | Model name passed to the active LLM provider |
additional_system_prompt | None | Appended to the default tool-use system prompt |
temperature | 0.7 | Forwarded to the provider when supported |
from qveris import Agent, QverisConfig, AgentConfig
agent = Agent(
config=QverisConfig(max_iterations=20),
agent_config=AgentConfig(model="gpt-4o", temperature=0.2),
)
QverisClient| Method | REST endpoint | Purpose |
|---|---|---|
discover(query, limit=20, session_id=None) | POST /search | Find capabilities with natural language (free) |
inspect(tool_ids, search_id=None, session_id=None) | POST /tools/by-ids | Fetch full capability metadata (free) |
call(tool_id, parameters, search_id=None, session_id=None, max_response_size=None) | POST /tools/execute | Execute a capability (may consume credits) |
usage(**filters) | GET /auth/usage/history/v2 | Audit request status and charge outcome |
ledger(**filters) | GET /auth/credits/ledger | Inspect final credit balance movements |
handle_tool_call(func_name, func_args, session_id=None) | — | Bridge an LLM tool call to the right QVeris method |
close() | — | Close the underlying HTTP client |
tool_ids accepts a single string or an iterable. usage(...) and ledger(...) take keyword-only filters such as start_date, end_date, summary (default True), bucket, charge_outcome, execution_id, search_id, direction, entry_type, min_credits, max_credits, limit, page, page_size.
Backward-compatible aliases remain available: search_tools → discover, get_tools_by_ids → inspect, execute_tool → call.
Agent| Member | Description |
|---|---|
run(messages, stream=True) | Async generator of StreamEvent; primary integration API |
run_to_completion(messages) | Non-streaming; returns the final assistant text |
get_last_messages() | Conversation history from the last run(...), including tool calls/results |
new_session() | Reset the correlation/session id |
close() | Close network resources (or use async with) |
Constructor: Agent(config=None, agent_config=None, llm_provider=None, extra_tools=None, extra_tool_handler=None, debug_callback=None).
The SDK returns Pydantic v2 models, so you get autocomplete and validation. Unknown backend fields are preserved, so newer API metadata will not break older SDK clients.
SearchResponse → results: list[ToolInfo]; ToolInfo has tool_id, name, description, params: list[ToolParameter], examples, stats, billing_rule.ToolExecutionResponse with execution_id, success, result, error_message, billing (CompactBillingStatement), cost, remaining_credits.UsageHistoryResponse → items: list[UsageEventItem], total, summary.CreditsLedgerResponse → items: list[CreditsLedgerItem], total, summary.from qveris import ToolExecutionResponse
def explain(result: ToolExecutionResponse) -> str:
if not result.success:
return f"failed: {result.error_message}"
charged = result.billing.summary if result.billing else "no billing info"
return f"ok ({charged}); remaining={result.remaining_credits}"
Use the level that matches your application:
discover/inspect/call/usage/ledger from your own code.Agent.run(messages) and consume StreamEvent values.Agent.run(messages, stream=False) for complete turns plus events.Agent.run_to_completion(messages).from qveris import QverisClient
from qveris.client.tools import DISCOVER_TOOL_DEF, INSPECT_TOOL_DEF, CALL_TOOL_DEF
tools = [DISCOVER_TOOL_DEF, INSPECT_TOOL_DEF, CALL_TOOL_DEF]
client = QverisClient()
# ... your LLM emits a tool call (func_name, func_args) ...
result, is_error, handled = await client.handle_tool_call(func_name, func_args)
if handled and not is_error:
... # feed result back to your model
The default Agent() uses the built-in OpenAI-compatible provider. For other model APIs, implement LLMProvider and pass it in:
from typing import AsyncGenerator, List
from openai.types.chat import ChatCompletionToolParam
from qveris import Agent
from qveris.config import AgentConfig
from qveris.llm.base import LLMProvider
from qveris.types import ChatResponse, Message, StreamEvent
class MyProvider(LLMProvider):
async def chat_stream(self, messages: List[Message], tools: List[ChatCompletionToolParam], config: AgentConfig) -> AsyncGenerator[StreamEvent, None]:
...
async def chat(self, messages: List[Message], tools: List[ChatCompletionToolParam], config: AgentConfig) -> ChatResponse:
...
agent = Agent(llm_provider=MyProvider())
httpx.HTTPStatusError; business-failure envelopes raise RuntimeError.error StreamEvent and end the run. run_to_completion(...) re-raises them as RuntimeError.result.success reflects the capability call only. Do not treat it as the final billing outcome — confirm charges with usage(...) / ledger(...).Runnable examples live under packages/python-sdk/examples/:
| Example | Scenario |
|---|---|
finance_research.py | Stock quote / market data research |
risk_compliance.py | Sanctions, adverse media, compliance screening |
crypto_market.py | Crypto price and volume data |
data_analysis.py | Dataset enrichment with external capability data |
agent_loop_integration.py | LLM agent loop integration |
Capability examples run discover/inspect when QVERIS_API_KEY is set, and only execute call when RUN_QVERIS_CALLS=1.
>=3.8.qveris on PyPIpackages/python-sdk