Docs
Early Access hello@polariapi.com
v0.2.0 · Beta
Getting Started

Python SDK

An async Python client for the Polari API. Handles authentication, the submit/poll/retrieve job pattern, retries, and cost tracking out of the box.

Beta SDK. The Python SDK has full support for all four layers — Layer 0 through Layer 3. Layer 3 (Intelligence Graph) requires a Professional or higher tier key.

Installation

Install via pip:

BASH
pip install polari-sdk

Authentication

Pass your API key directly to the client, or set the POLARI_API_KEY environment variable and use from_env().

PYTHON
from polari import PolariClient # Explicit key client = PolariClient(api_key="pk_live_your_key") # From environment variable POLARI_API_KEY client = PolariClient.from_env()

Basic usage

The client is async. Use it as a context manager to ensure the underlying HTTP connection is properly closed.

PYTHON
import asyncio from polari import PolariClient, ArticleInput async def main(): async with PolariClient(api_key="pk_live_your_key") as client: article = ArticleInput( title="Fed Holds Rates Steady", content="The Federal Reserve held interest rates steady on Wednesday, as policymakers...", url="https://reuters.com/fed-rates-2026", source="Reuters", ) result = await client.layer0.analyze(article) print(f"Quality: {result.quality_score:.3f}") # → 0.810 print(f"Tokens: {result.token_count}") # → 842 print(f"ID: {result.article_id}") # → art_8f7h2k9s asyncio.run(main())

The SDK abstracts the submit/poll/retrieve pattern — analyze() handles polling internally and returns when processing is complete.


Batch processing

Process multiple articles with analyze_batch(). Articles are submitted in a single batch request, then all jobs are polled concurrently — significantly faster than processing sequentially. The batch_size parameter controls how many are sent per API call (max 50).

PYTHON
articles = [ ArticleInput(title="Article One", content="...", source="Reuters"), ArticleInput(title="Article Two", content="...", source="Bloomberg"), ArticleInput(title="Article Three", content="...", source="AP"), ] results = await client.layer0.analyze_batch(articles, batch_size=10) for r in results: print(f"{r.article_id} quality={r.quality_score:.2f}")

Full pipeline

Each layer builds on the previous. Layer 1 requires a Layer 0 article ID; Layer 2 requires both Layer 0 and Layer 1 to have run first.

PYTHON
async with PolariClient(api_key="pk_live_your_key") as client: article = ArticleInput( title="Fed Holds Rates Steady", content="The Federal Reserve held interest rates steady...", url="https://reuters.com/fed-rates-2026", source="Reuters", ) # Layer 0 — quality score + token embeddings l0 = await client.layer0.analyze(article) print(f"Quality: {l0.quality_score:.3f} ID: {l0.article_id}") # Layer 1 — entity extraction + sentence semantics l1 = await client.layer1.process( article_id=l0.article_id, title=article.title, content=article.content, ) print(f"Entities: {l1.stats.entity_count} Sentences: {l1.stats.sentence_count}") print(f"People: {l1.entities.get('PERSON', [])}") # Layer 2 — story clustering l2 = await client.layer2.cluster(l0.article_id) print(f"Cluster: {l2.cluster_id} Confidence: {l2.confidence:.3f} ") # Layer 3 — intelligence graph (Pro+ only) trends = await client.layer3.get_trending_entities(limit=10) for t in trends["trends"]: print(f"{t['entity']}: velocity={t['velocity']}")

Configuration

Fine-tune timeouts and retry behavior at client initialization. All layer URLs default to the Polari API endpoints.

PYTHON
from polari import PolariClient client = PolariClient( api_key="pk_live_your_key", timeout=60, # seconds max_retries=3, enable_metrics=True, enable_cost_tracking=True, )

Result object

analyze() returns a Layer0Result with the following fields:

Field Type Description
article_id string Unique article identifier for use in downstream API calls
quality_score float 0–1 content quality. Articles below 0.53 are filtered before Layer 1 and Layer 2.
token_count integer Article length in tokens
semantic_hash string Content fingerprint for deduplication
embedding List[float] 384-dim token embedding. Returns [0.0] * 384 unless include_embedding=True is passed to analyze().
is_duplicate bool Whether this URL was already processed. If True, the returned article_id is the existing article — still valid for Layer 1 and Layer 2.
processing_time float End-to-end processing time in seconds

Embeddings

By default, the 384-dimensional token embedding is not included in the response. Pass include_embedding=True to fetch it from the vector store. Note this adds latency as the embedding is retrieved from ChromaDB.

PYTHON
result = await client.layer0.analyze(article, include_embedding=True) result.embedding # List[float], length 384 result.token_count # number of tokens the embedding was averaged from

If include_embedding=False (default), result.embedding returns [0.0] * 384.


Error handling

The SDK raises structured exceptions. AuthenticationError and ValidationError are not retried — all other errors use exponential backoff with jitter.

PYTHON
from polari.exceptions import ( AuthenticationError, RateLimitError, ValidationError, RetryExhaustedError, PolariError, ) try: result = await client.layer0.analyze(article) except AuthenticationError: print("Invalid API key") except RateLimitError: print("Rate limit hit — back off and retry") except ValidationError as e: print(f"Bad request: {e}") # e.g. text too short except RetryExhaustedError: print("Max retries exceeded") except PolariError as e: print(f"Unexpected error: {e}")

Exception hierarchy

HIERARCHY
PolariError ├── PolariAPIError(status_code, message) │ ├── RateLimitError # 429 — retried with backoff │ ├── AuthenticationError # 401 — not retried │ ├── ValidationError # 400/422 — not retried │ └── ServerError # 5xx — retried with backoff ├── NetworkError # connectivity — retried ├── TimeoutError # request timeout — retried ├── ConfigurationError # invalid config — not retried ├── ProcessingError # article processing failure └── RetryExhaustedError # max retries exceeded

Metrics & cost tracking

The client tracks per-request latency, success rate, and per-layer cost automatically.

PYTHON
# After processing some articles... metrics = client.get_metrics() print(metrics.total_requests) # → 42 print(metrics.average_latency) # → 1.24s print(metrics.successful_requests) # → 41 cost = client.get_cost_summary() print(cost.total_cost) # → $0.042 print(cost.cost_by_layer) # → {"layer0": 0.042}
Layer Cost per call
Layer 0 $0.001
Layer 1 $0.002
Layer 2 $0.001
Layer 3 $0.003

ArticleInput reference

Field Type Description
contentrequired string Article body. Minimum 100 characters.
titlerequired string Article headline
url string Canonical URL — used as deduplication key when provided
source string Publisher name
author string Byline
published_at datetime Original publication time
metadata dict Arbitrary key/value pairs passed through to the result