---
title: Python SDK
description: Install and use the PostGrad Python SDK.
---

Typed Python client built on httpx and Pydantic v2. Ships with `py.typed` for IDE autocomplete, automatic retries on 429, typed exceptions, and a `default_feed` option.

## Installation

```bash
pip install postgrad
```

## Quick Start

```python
from postgrad import PostGrad

# default_feed="all" makes every search query fan out across all of
# your subscribed feeds. Alternatively pass a specific feed UUID, or
# omit default_feed to have PostGrad auto-select your most-populated feed.
client = PostGrad(api_key="pg_live_YOUR_API_KEY", default_feed="all")

# Search across all subscribed feeds and merge results by relevance.
# Each hit's metadata includes feed_id + feed_name.
results = client.search("pricing strategy")
for entry in results.data:
    print(f"{entry.title} (from {entry.feed_name}, {entry.confidence})")

# Override default_feed per-call when you want to scope to one feed.
results = client.search("pricing strategy", feed_id="b1a2c3d4-e5f6-7890-abcd-ef0123456789")

# List + get endpoints still require an explicit feed id.
entries = client.knowledge.list(
    feed_id="b1a2c3d4-e5f6-7890-abcd-ef0123456789",
    category="deal_evaluation",
    limit=10,
)

entry = client.knowledge.get(
    "entry-uuid-here",
    feed_id="b1a2c3d4-e5f6-7890-abcd-ef0123456789",
)
print(entry.data.content)
```

## Client Options

```python
client = PostGrad(
    api_key="pg_live_YOUR_API_KEY",
    base_url="https://postgrad.io",  # default
    timeout=30,  # request timeout in seconds (default: 30)
    # default_feed applies to every request unless overridden per-call.
    # Three accepted values:
    #   - "all" — fan out across every subscribed feed (search only)
    #   - a feed UUID — scope to that feed
    #   - None (omitted) — auto-select your most-populated feed (search only)
    default_feed="all",
)
```

## Methods

### `client.search(query, **kwargs)`

Search the knowledge base with natural language.

```python
results = client.search(
    "AI agent architecture",
    categories=["ai_architecture", "tech_stack"],
    confidence_min=0.7,
    limit=20,
)

for entry in results.data:
    print(f"{entry.title} ({entry.confidence})")
```

### `client.knowledge.list(**kwargs)`

List knowledge entries with optional filters.

```python
entries = client.knowledge.list(
    category="sales_process",
    limit=25,
    offset=0,
)
```

### `client.knowledge.get(id)`

Retrieve a single knowledge entry by ID.

```python
entry = client.knowledge.get("550e8400-e29b-41d4-a716-446655440000")
print(entry.data.title)
print(entry.data.content)
```

### `client.categories.list()`

List all available knowledge categories with entry counts.

```python
categories = client.categories.list()
for cat in categories.data:
    print(f"{cat.name}: {cat.count} entries")
```

### `client.usage.get()`

Check your current billing period usage.

```python
usage = client.usage.get()
print(f"{usage.data.queries_used} / {usage.data.queries_limit} queries used")
```

## Error Handling

The SDK raises typed exceptions for common failure scenarios.

```python
from postgrad import PostGrad, PostGradError, RateLimitError, AuthError

client = PostGrad(api_key="pg_live_YOUR_API_KEY")

try:
    results = client.search("pricing")
except AuthError:
    print("Invalid or expired API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except PostGradError as e:
    print(f"API error: {e.message} ({e.status})")
```

## Pydantic Models

All response objects are Pydantic models with full type information.

```python
from postgrad.types import KnowledgeEntry, Category, UsageInfo

# KnowledgeEntry fields
entry: KnowledgeEntry
entry.id          # str (UUID)
entry.title       # str
entry.category    # str
entry.content     # str
entry.tags        # list[str]
entry.confidence  # float (0-1)
entry.reinforcement_count  # int
entry.created_at  # datetime
entry.updated_at  # datetime

# Category fields
cat: Category
cat.name   # str
cat.count  # int

# UsageInfo fields
usage: UsageInfo
usage.period_start      # datetime
usage.period_end        # datetime
usage.queries_used      # int
usage.queries_limit     # int
usage.queries_remaining # int
usage.tier              # str
```

## Using the REST API Directly

If you prefer not to wait for the SDK, you can use `requests` or `httpx` directly:

```python
import requests

API_KEY = "pg_live_YOUR_API_KEY"
BASE_URL = "https://postgrad.io/api/v1"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    # "all" = fan out across every feed you're subscribed to.
    # Replace with a feed UUID to scope to one feed, or drop the header
    # entirely to let PostGrad auto-select.
    "X-PostGrad-Feed": "all",
}

# Search
response = requests.get(
    f"{BASE_URL}/knowledge/search",
    headers=headers,
    params={"q": "pricing strategy", "limit": 10},
)
data = response.json()

for entry in data["data"]:
    print(entry["title"])
```
