Skip to main content

Knowhere Python SDK

PyPI version

Official Python SDK for the Knowhere document parsing API.

Installation

pip install knowhere-python-sdk

Or with uv:

uv add knowhere-python-sdk

Usage

import knowhere

client = knowhere.Knowhere(api_key="sk_...")

result = client.parse(
    url="https://example.com/report.pdf",
)

print(result.statistics.total_chunks)
print(result.full_markdown[:200])

for chunk in result.text_chunks:
    print(chunk.content[:80])

for page in result.page_chunks:
    print(page.content_source)       # "summary"
    print(page.content[:120])        # page-level summary
    print(page.metadata.page_nums)   # [4, 5, 6]

Retrieval and document lifecycle

New documents are published into a retrieval namespace. The server returns a stable document_id on job create when it has a planned id, and on the completed job_result after publication.

job = client.jobs.create(
    source_type="url",
    source_url="https://example.com/manual.pdf",
    namespace="support-center",
    document_metadata={"title": "Support manual"},
)

document_id = job.document_id
job_result = client.jobs.wait(job.job_id)
document_id = document_id or job_result.document_id

if document_id is None:
    raise RuntimeError("Expected document_id after successful publication.")

After the job is done and published, query the canonical document content:

response = client.retrieval.query(
    namespace="support-center",
    query="How do I reset Bluetooth pairing?",
    chunk_types=["page"],
    top_k=5,
    channels=["path", "term"],
    filter_mode="keep",
    signal_paths=["Bluetooth", "Pairing"],
)

print(response.router_used)
print(response.answer_text)
print(response.evidence_text)
print(response.stop_reason)
print(response.failure_reason)

for reference in response.referenced_chunks:
    print(reference.chunk_id, reference.chunk_type, reference.content_source)
    print(reference.metadata, reference.asset_url)

for result in response.results:
    print(result.chunk_id, result.chunk_type, result.content_source)
    print(result.content)
    print(result.score)
    print(result.source.source_file_name, result.source.section_path)

Use document_id to update or archive a document:

update_job = client.jobs.create(
    source_type="url",
    source_url="https://example.com/manual-v2.pdf",
    document_id=document_id,
)

document = client.documents.get(document_id)
print(document.status)

chunks = client.documents.list_chunks(
    document_id,
    page=1,
    page_size=50,
    chunk_type="page",
    include_asset_urls=True,
)
print(chunks.pagination.total)
if chunks.chunks:
    chunk = client.documents.get_chunk(
        document_id,
        chunks.chunks[0].id,
        include_asset_urls=True,
    )
    print(chunk.chunk.content)
    print(chunk.chunk.metadata.get("page_nums"))  # Page citations.
    print(chunk.chunk.asset_url)  # Requested 7-day URL when available.
    page_assets = chunk.chunk.metadata.get("pageAssets") or []
    print(page_assets)

source = client.documents.get_page_citation_source(document_id)
print(source.url)

client.documents.archive(document_id)

You can also list documents in a namespace:

documents = client.documents.list(
    namespace="support-center",
    page=1,
    page_size=50,
)
for document in documents.documents:
    print(document.document_id, document.status)
print(documents.pagination.total_pages)

Retrieval can limit documents for one request and exclude documents or sections. Omitting include_document_ids leaves documents unrestricted by inclusion; passing [] matches no documents. Exclusions take precedence over inclusions.

response = client.retrieval.query(
    namespace="support-center",
    query="battery charging",
    include_document_ids=["doc_123", "doc_old"],
    exclude_document_ids=["doc_old"],
    exclude_sections=[
        {"document_id": "doc_123", "section_path": "Appendix / Legal"}
    ],
)

While you can provide an api_key keyword argument, we recommend using python-dotenv to add KNOWHERE_API_KEY="sk_..." to your .env file so that your API key is not stored in source control.

Short-lived dashboard tokens can use auth_token_provider instead of a static key. If api_key is also set, the static key wins.

client = knowhere.Knowhere(auth_token_provider=lambda: current_access_token())

Parse a local file

from pathlib import Path

result = client.parse(
    file=Path("report.pdf"),
    parsing_params={"model": "advanced", "ocr_enabled": True},
)

print(result.manifest.source_file_name)  # "report.pdf"
print(len(result.chunks))                # 152
print(result.namespace)                  # "default" or your explicit namespace
print(result.document_id)                # Published canonical document id

Bring your own LLM keys (BYOK)

Pass OpenAI-compatible credentials for parsing or agentic retrieval.

Flat root applies to both channels (one multimodal model). Use models for different model ids on the same endpoint, or text / vision for different provider endpoints:

# Multimodal shorthand — one model for text + vision
llm_config = {
    "api_key": "sk-...",
    "model": "gpt-4o",
    "base_url": "https://api.openai.com/v1",
}

# Same endpoint, different models per channel
llm_config = {
    "api_key": "sk-...",
    "base_url": "https://api.openai.com/v1",
    "models": {"text": "gpt-4o-mini", "vision": "gpt-4o"},
}

# Or two different endpoints
llm_config = {
    "text": {
        "api_key": "sk-...",
        "model": "gpt-4o-mini",
        "base_url": "https://api.openai.com/v1",
    },
    "vision": {
        "api_key": "sk-ali-...",
        "model": "qwen-vl-max",
        "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
    },
}

result = client.parse(file=Path("report.pdf"), llm_config=llm_config)

response = client.retrieval.query(
    namespace="support-center",
    query="refund policy",
    use_agentic=True,
    llm_config=llm_config,
)

Access different chunk types

result = client.parse(url="https://example.com/report.pdf")

# Text chunks
for chunk in result.text_chunks:
    print(chunk.metadata.keywords)
    print(chunk.metadata.summary)

# Page chunks (v2 page-memory results)
for chunk in result.page_chunks:
    print(chunk.content_source)      # "summary"
    print(chunk.content[:120])
    print(chunk.metadata.page_nums)  # citation pages
    print(chunk.metadata.entities)

# Image chunks (raw bytes loaded from ZIP)
for chunk in result.image_chunks:
    print(chunk.file_path)
    print(len(chunk.data))       # bytes
    chunk.save("./output/")      # writes image to disk

# Table chunks (HTML loaded from ZIP)
for chunk in result.table_chunks:
    print(chunk.file_path)
    print(chunk.html[:100])

Save all results to disk

result = client.parse(file=Path("report.pdf"))
result.save("./output/report/")

Async usage

import asyncio
import knowhere

async def main():
    async with knowhere.AsyncKnowhere(api_key="sk_...") as client:
        result = await client.parse(url="https://example.com/report.pdf")
        print(result.statistics.total_chunks)

        for chunk in result.text_chunks:
            print(chunk.summary)

asyncio.run(main())

Step-by-step control

For granular control over the parsing workflow, use the jobs resource directly:

from pathlib import Path

# Step 1: Create a parsing job
job = client.jobs.create(
    source_type="file",
    file_name="report.pdf",
    namespace="support-center",
    parsing_params={"model": "advanced", "ocr_enabled": True},
)

# Step 2: Upload file to presigned URL
client.jobs.upload(job, file=Path("report.pdf"))

# Step 3: Poll until done (adaptive backoff)
job_result = client.jobs.wait(job.job_id, poll_interval=10.0, poll_timeout=1800.0)

print(job_result.document_id)  # Persist this to update/archive the document later.

# Step 4: Download and parse results
result = client.jobs.load(job_result)
print(result.statistics)

Handling errors

All errors inherit from knowhere.KnowhereError.

import knowhere

try:
    result = client.parse(url="https://example.com/report.pdf")
except knowhere.AuthenticationError:
    print("Invalid API key")
except knowhere.APIStatusError as e:
    print(f"{e.status_code}: {e.message}")

Configuration

The SDK reads configuration from constructor arguments, environment variables, or defaults (in that priority order):

Variable Description Default
KNOWHERE_API_KEY API key (required) —
KNOWHERE_BASE_URL API base URL https://api.knowhereto.ai
KNOWHERE_LOG_LEVEL Log level WARNING
# Uses environment variables automatically
client = knowhere.Knowhere()

# Or configure explicitly
client = knowhere.Knowhere(
    api_key="sk_...",
    base_url="https://api.knowhereto.ai",
    timeout=30.0,           # HTTP request timeout (default: 60s)
    upload_timeout=300.0,   # File upload timeout (default: 600s)
    max_retries=3,          # Max retry attempts (default: 5)
)

Retries

Connection errors, 429 Rate Limit, and >=500 Internal errors are automatically retried with exponential backoff.

client = knowhere.Knowhere(
    api_key="sk_...",
    max_retries=3,  # default is 5
)

Determining the installed version

import knowhere
print(knowhere.__version__)

Versioning

This package follows Semantic Versioning.

We publish stable releases to PyPI. To install the latest unreleased changes directly from the repository: https://github.com/Ontos-AI/knowhere-python-sdk

Requirements

Community

License

MIT

Metadata

Release files for knowhere-python-sdk 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for knowhere-python-sdk 2.1.0
File Size Uploaded
knowhere_python_sdk-2.1.0.tar.gz 539.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for knowhere-python-sdk 2.1.0
File Interpreter ABI Platform
knowhere_python_sdk-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 581.0 kB

Release files / knowhere_python_sdk-2.1.0.tar.gz

Download URL knowhere_python_sdk-2.1.0.tar.gz
Size 539.5 kB
Tags Source
SHA-256 checksum
How to use checksums
7254ba3d7ed3a0e9fd84a21d4444e933e151cb3c6b41dfe5169215868a4bfe98
BLAKE2b-256 checksum
How to use checksums
7483439b0a04e7f95ea6d2879a42ca16f4d47c6f3238f0b8b188d275e9f81b2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / knowhere_python_sdk-2.1.0-py3-none-any.whl

Download URL knowhere_python_sdk-2.1.0-py3-none-any.whl
Size 41.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
04e41bdd4498ba8e348ab11dac33db194c55020f499485ce398e6d6a077dcbd6
BLAKE2b-256 checksum
How to use checksums
2c0a447e093b050e228ace725227fd4bd08925b5c859dca0741aef8c5fcf6da8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

2.2.0

2 release files

This release

2.1.0 This release

2 release files

2.0.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page