New1 billion tokens free on sign-up — crawl, parse, chunk and embed on the houseClaim yours
SarvCrawl
Docs/Python SDK
SarvCrawl / Docs /Python SDK

Python SDK Reference

Complete method reference with request examples and responses.

pip install sarvcrawl

Installation & Initialization

Install the SDK and create a client. Pass your API key directly or via the KB_API_KEY environment variable.

KBClient(api_key, api_url, ...)

Synchronous client. Raises ValueError if no API key is provided.

from sarvcrawl import KBClient

kb = KBClient(
    api_key='YOUR_API_KEY',        # or set KB_API_KEY env var
    api_url='https://crawl.sarv.com',  # the default; set your own host to self-host
    timeout=30,
    max_retries=3,
)
kb.health()
GET/health

Returns server health status.

status = kb.health()
print(status)
kb.me()
GET/auth/me

Returns the authenticated user profile.

user = kb.me()
print(user['id'], user['email'])

Knowledge Bases

Create and manage knowledge bases that store your scraped and processed content.

kb.create_kb(name, description?, settings?)KnowledgeBase
POST/api/kb

Creates a new knowledge base.

new_kb = kb.create_kb(
    'Research KB',
    description='My research knowledge base',
)
print(new_kb.id)  # 'kb_abc123'
kb.list_kbs()List[KnowledgeBase]
GET/api/kb

Lists all knowledge bases owned by the authenticated user.

kbs = kb.list_kbs()
for k in kbs:
    print(k.id, k.name)
kb.get_kb(kb_id)KnowledgeBase
GET/api/kb/:id
my_kb = kb.get_kb('kb_abc123')
print(my_kb.name)
kb.get_kb_stats(kb_id)KBStats
GET/api/kb/:id/stats
stats = kb.get_kb_stats('kb_abc123')
print(stats.total_pages, stats.total_mb)
kb.delete_kb(kb_id)
DELETE/api/kb/:id

Permanently deletes a knowledge base and all its contents.

kb.delete_kb('kb_abc123')

Scrape

Extract content from a single URL and store it in a knowledge base. Returns a JobRef — poll get_job() to track progress.

kb.scrape(kb_id, url, formats?, format_options?, include_docs?)JobRef
POST/api/jobs/scrape
job = kb.scrape(
    'kb_abc123',
    url='https://example.com/article',
    formats=['markdown', 'html'],  # default: ['markdown', 'json']
    include_docs=True,             # auto-download linked PDFs/DOCX
)
print(job.job_id, job.status)  # 'queued'

Crawl

Recursively crawl a website from a root URL and store every discovered page.

kb.crawl(kb_id, url, formats?, ..., exclude_paths?)JobRef
POST/api/jobs/crawl
job = kb.crawl(
    'kb_abc123',
    url='https://docs.example.com',
    formats=['markdown', 'json'],
    max_depth=5,
    limit=500,
    exclude_paths=['/admin', '/api'],
    include_docs=True,
)
print(job.job_id)

Map

Discover all URLs on a site without extracting page content.

kb.map_job(kb_id, url, limit?)JobRef
POST/api/jobs/map
job = kb.map_job(
    'kb_abc123',
    url='https://example.com',
    limit=5000,  # default: 1000
)
print(job.job_id)

Upload

Parse and index a local file — PDF, DOCX, XLSX, CSV, images, and more. OCR fallback is applied for scanned PDFs.

kb.upload(kb_id, file_path, formats?, ocr_language?)JobRef
POST/api/jobs/upload
job = kb.upload(
    'kb_abc123',
    file_path='/path/to/report.pdf',
    formats=['markdown', 'json'],
    ocr_language='eng',  # default: 'eng'. Also supports 'hin'
)
print(job.job_id)

Monitor

Create a recurring check that re-scrapes a page (or re-crawls a site), diffs it against the last check, and POSTs a webhook when something is new, changed, or removed. Unlike other jobs, a monitor never "completes" — it stays "running" indefinitely by design. Output is always markdown, there's no formats/scrapeOptions field.

kb.monitor(kb_id, monitor_type, schedule, notify_url, urls?, url?, limit?, include_paths?, exclude_paths?)JobRef
POST/api/jobs/monitor

"page" mode watches one or more exact URLs (pass urls). "website" mode crawls a whole site and watches every discovered page (pass url, plus optional limit/include_paths/exclude_paths). schedule accepts natural language ("daily", "every 30 minutes") or a cron expression — a minimum interval is enforced server-side.

job = kb.monitor(
    'kb_abc123',
    monitor_type='page',
    urls=['https://example.com/pricing'],
    schedule='daily',
    notify_url='https://your-endpoint.example.com/webhook',
)
print(job.job_id, job.status)  # "queued"

# Website mode — crawl and watch every discovered page:
kb.monitor(
    'kb_abc123',
    monitor_type='website',
    url='https://docs.example.com',
    limit=100,
    include_paths=['/docs'],
    schedule='every 6 hours',
    notify_url='https://your-endpoint.example.com/webhook',
)

Job Management

Poll, list, cancel, and inspect jobs. Status transitions: queued → running → completed / failed.

kb.get_job(job_id)Job
GET/api/jobs/:id

Fetches full details and current status for a job.

job = kb.get_job('dacf0540-f611-47eb-8e47-d5c72acea679')
print(job.status, job.urls_processed)
kb.list_jobs(kb_id?, status?, type?, limit?, offset?)List[Job]
GET/api/jobs

Lists jobs with optional filters.

jobs = kb.list_jobs(
    kb_id='kb_abc123',
    status='completed',  # queued | running | completed | failed | cancelled
    type='scrape',       # scrape | crawl | search | map | upload
    limit=20,
    offset=0,
)
kb.cancel_job(job_id)
DELETE/api/jobs/:id/cancel

Cancels a running or queued job. For a monitor job (which stays "running" indefinitely by design), this is the correct way to stop the recurring check — required before delete_job() will succeed.

kb.cancel_job('dacf0540-f611-47eb-8e47-d5c72acea679')
kb.delete_job(job_id)
DELETE/api/jobs/:id

Permanently deletes a job and its stored pages. Refuses jobs in "running"/"queued" status — monitor jobs must be cancelled with cancel_job() first.

kb.delete_job('dacf0540-f611-47eb-8e47-d5c72acea679')
kb.get_job_logs(job_id)List[JobLog]
GET/api/jobs/:id/logs

Returns processing logs for a job.

logs = kb.get_job_logs('dacf0540-f611-47eb-8e47-d5c72acea679')
for log in logs:
    print(log.level, log.message)
kb.get_job_audit(job_id)AuditReport
GET/api/jobs/:id/audit

Returns a diff-audit comparing source PDF against extracted markdown.

audit = kb.get_job_audit('dacf0540-f611-47eb-8e47-d5c72acea679')
print(audit.similarity_score)
kb.get_job_source_pdf(job_id)bytes
GET/api/jobs/:id/source-pdf

Downloads the original source PDF for an upload job.

pdf_bytes = kb.get_job_source_pdf('dacf0540-f611-47eb-8e47-d5c72acea679')
with open('source.pdf', 'wb') as f:
    f.write(pdf_bytes)
kb.get_job_source_md(job_id)str
GET/api/jobs/:id/source-md

Returns the extracted markdown for an upload job.

md = kb.get_job_source_md('dacf0540-f611-47eb-8e47-d5c72acea679')
print(md[:500])

Pages & Search

Browse pages stored in a knowledge base, retrieve their content, and run full-text search.

kb.list_pages(kb_id, options?)List[PageSummary]
GET/api/kb/:id/pages
pages = kb.list_pages('kb_abc123', limit=50, offset=0)
for p in pages:
    print(p.url, p.title)
kb.get_page(kb_id, page_id, format?)PageContent
GET/api/kb/:id/pages/:pageId

Fetches the stored content of a page. format defaults to 'json'.

page = kb.get_page('kb_abc123', 'page_abc', format='markdown')
print(page.content)
kb.search_kb(kb_id, q, options?)SearchResponse
GET/api/kb/:id/search

Full-text Elasticsearch search over all pages in a knowledge base.

results = kb.search_kb(
    'kb_abc123',
    q='knowledge infrastructure',
    page=1,
    size=10,
    type='scrape',     # optional: filter by job type
    job_id='job_xyz',  # optional: filter by specific job
)
for hit in results.hits:
    print(hit.title, hit.score)
kb.embed_search_kb(kb_id, q, options?)EmbedSearchResponse
GET/api/kb/:id/search/embed

Semantic (hybrid) search over chunk embeddings — dense KNN + BM25 fused with RRF. Returns chunk-level hits ideal for RAG. Not paginated (no page param).

results = kb.embed_search_kb(
    'kb_abc123',
    q='how do refunds work?',
    size=10,
    type='crawl',      # optional: filter by job type
    job_id='job_xyz',  # optional: filter by specific job
)
for hit in results.hits:
    print(hit.rrf_score, hit.title, hit.text)

Files & Exports

Browse stored files, download individual files, and export jobs or entire knowledge bases.

kb.list_kb_jobs(kb_id, options?)List[Job]
GET/api/kb/:id/jobs

Lists all jobs belonging to a specific knowledge base.

jobs = kb.list_kb_jobs('kb_abc123', limit=20, offset=0)
kb.list_job_files(kb_id, job_id)FileListResponse
GET/api/kb/:id/jobs/:jobId/files

Lists all files stored by a job.

files = kb.list_job_files('kb_abc123', 'dacf0540-f611-47eb-8e47-d5c72acea679')
for f in files.files:
    print(f.path, f.size)
kb.download_file(kb_id, job_id, file_path)bytes
GET/api/kb/:id/jobs/:jobId/files/:fp

Downloads a single stored file by its storage path.

data = kb.download_file(
    'kb_abc123',
    'dacf0540-f611-47eb-8e47-d5c72acea679',
    'pages/page_abc/content.md',
)
with open('content.md', 'wb') as f:
    f.write(data)
kb.export_job_zip(kb_id, job_id)bytes
GET/api/kb/:id/jobs/:jobId/export.zip

Downloads all files from a single job as a ZIP archive.

zip_bytes = kb.export_job_zip('kb_abc123', 'dacf0540-f611-47eb-8e47-d5c72acea679')
with open('job-export.zip', 'wb') as f:
    f.write(zip_bytes)
kb.export_kb_zip(kb_id)bytes
GET/api/kb/:id/export.zip

Downloads all pages across every job in a knowledge base as a single ZIP archive.

zip_bytes = kb.export_kb_zip('kb_abc123')
with open('kb-export.zip', 'wb') as f:
    f.write(zip_bytes)
kb.export_kb(kb_id, format?)Generator[bytes]
GET/api/kb/:id/export

Streams a full KB export as JSONL or CSV. format is 'jsonl' (default) or 'csv'.

with open('kb-export.jsonl', 'wb') as f:
    for chunk in kb.export_kb('kb_abc123', format='jsonl'):
        f.write(chunk)

AsyncKBClient

All methods are available on AsyncKBClient with identical signatures. Use with async/await in async contexts.

AsyncKBClient

Drop-in async replacement for KBClient. Import and initialize the same way.

import asyncio
from sarvcrawl import AsyncKBClient

async def main():
    kb = AsyncKBClient(api_key='YOUR_API_KEY')

    job = await kb.scrape('kb_abc123', 'https://example.com')
    print(job.job_id)

    # All methods mirror the sync client
    jobs = await kb.list_jobs(kb_id='kb_abc123', status='completed')
    pages = await kb.list_pages('kb_abc123')
    results = await kb.search_kb('kb_abc123', 'knowledge infrastructure')
    semantic = await kb.embed_search_kb('kb_abc123', 'how do refunds work?')

    # Streaming export
    with open('export.jsonl', 'wb') as f:
        async for chunk in kb.export_kb('kb_abc123'):
            f.write(chunk)

asyncio.run(main())