---
title: Quickstart (Python)
description: Get a CodeSpar commerce agent running in Python — sync for scripts and Django, async for FastAPI and LangChain. Under 5 minutes.
---

import { Callout } from "fumadocs-ui/components/callout";
import { Steps, Step } from "fumadocs-ui/components/steps";
import { Tab, Tabs } from "fumadocs-ui/components/tabs";

# Quickstart (Python)

<VersionBadge pkg="codespar" />

The `codespar` PyPI package exposes the same session model as the TypeScript SDK, in both sync and async flavours. Use `CodeSpar` from scripts, Jupyter notebooks, Flask, or Django views. Use `AsyncCodeSpar` from FastAPI, LangChain, or anything already on asyncio.

## Prerequisites

- Python 3.10+
- A CodeSpar API key — mint one at [Dashboard → API Keys](https://codespar.dev/dashboard/api-keys). New accounts get a `test`-environment project plus a `csk_test_*` key auto-created at signup.
- Optional: an LLM SDK (`anthropic`, `openai`) if you want to build a full agent loop

<Steps>

<Step>
### Install

```bash
pip install codespar
```

Or add to `pyproject.toml`:

```toml
[project]
dependencies = ["codespar>=0.10.0"]
```

The package has a single runtime dependency (`httpx`). No async framework is pulled in — both flavours ship in the same wheel.
</Step>

<Step>
### Set your API key

```bash
export CODESPAR_API_KEY="csk_test_your_key_here"
```

A `csk_test_*` key authorizes against a `test`-environment project. Tool calls route to the catalog's `base_url_test` provider sandbox where one is defined, and to the real production endpoint otherwise. For deterministic responses without any upstream call, declare per-session fixtures via `cs.create({ mocks: {...} })` — see [Test Mode](/docs/concepts/test-mode) for the full reference.
</Step>

<Step>
### Run a session against an inline mock

The fastest way to confirm the SDK is wired correctly — and the pattern most tests use — is to declare a mock on session create. The runtime substitutes the fixture for the upstream call; policy, audit, and commerce-memory still fire on every invocation.

<Tabs items={["Sync", "Async"]}>
<Tab value="Sync">
```python title="test_mode.py"
# Requires codespar==0.10.0+
from codespar import CodeSpar
import os

cs = CodeSpar(api_key=os.environ["CODESPAR_API_KEY"])

with cs.create("user_test", {
    "servers": ["asaas"],
    "mocks": {
        "asaas/create_payment": {"id": "pay_test_42", "status": "PENDING"},
    },
}) as session:
    result = session.execute("asaas/create_payment", {"value": 100})
    print(result["id"])  # "pay_test_42"

cs.close()
```
</Tab>
<Tab value="Async">
```python title="test_mode.py"
# Requires codespar==0.10.0+
import asyncio
import os
from codespar import AsyncCodeSpar

async def main():
    async with AsyncCodeSpar(api_key=os.environ["CODESPAR_API_KEY"]) as cs:
        async with await cs.create("user_test", {
            "servers": ["asaas"],
            "mocks": {
                "asaas/create_payment": {"id": "pay_test_42", "status": "PENDING"},
            },
        }) as session:
            result = await session.execute("asaas/create_payment", {"value": 100})
            print(result["id"])  # "pay_test_42"

asyncio.run(main())
```
</Tab>
</Tabs>

A non-empty `mocks` field puts the session into strict mode for its lifetime: any tool call whose canonical name is not declared returns a `tool_not_mocked` envelope rather than falling through to the real upstream. See [Test Mode](/docs/concepts/test-mode) for the strict-mode contract, the five `tool_result` envelopes, and how to assert on them in Python (`is_policy_denied`, `is_approval_required`, `is_mocks_exhausted`, `is_tool_not_mocked`).
</Step>

<Step>
### Create a session + call a tool

<Tabs items={["Sync", "Async"]}>
<Tab value="Sync">
```python title="charge.py"
from codespar import CodeSpar

cs = CodeSpar()  # reads CODESPAR_API_KEY from env

with cs.create("user_123", preset="brazilian") as session:
    result = session.execute(
        "codespar_pay",
        {"method": "pix", "amount": 15000, "currency": "BRL"},
    )
    print(result.data["pix_code"])

cs.close()
```

Context-manager style closes the session automatically. Or call `session.close()` and `cs.close()` manually.
</Tab>
<Tab value="Async">
```python title="charge.py"
import asyncio
from codespar import AsyncCodeSpar

async def main():
    async with AsyncCodeSpar() as cs:
        async with await cs.create("user_123", preset="brazilian") as session:
            result = await session.execute(
                "codespar_pay",
                {"method": "pix", "amount": 15000, "currency": "BRL"},
            )
            print(result.data["pix_code"])

asyncio.run(main())
```
</Tab>
</Tabs>
</Step>

<Step>
### Natural-language agent loop

`session.send(message)` runs a Claude tool-use loop on the backend. No local agent framework needed.

<Tabs items={["Sync", "Async"]}>
<Tab value="Sync">
```python title="agent.py"
from codespar import CodeSpar

cs = CodeSpar()
with cs.create("user_123", preset="brazilian") as session:
    result = session.send(
        "Charge R$150 via Pix for order #5678 and send a WhatsApp confirmation"
    )
    print(result.message)
    for call in result.tool_calls:
        print(f"  → {call.tool_name}: {call.status} in {call.duration_ms}ms")
cs.close()
```
</Tab>
<Tab value="Async">
```python title="agent.py"
import asyncio
from codespar import AsyncCodeSpar

async def main():
    async with AsyncCodeSpar() as cs:
        async with await cs.create("user_123", preset="brazilian") as session:
            result = await session.send(
                "Charge R$150 via Pix for order #5678 and send a WhatsApp confirmation"
            )
            print(result.message)

asyncio.run(main())
```
</Tab>
</Tabs>
</Step>

<Step>
### Streaming

`send_stream` yields events as they happen — useful when you want to stream partial output to the user or to a log.

<Tabs items={["Sync", "Async"]}>
<Tab value="Sync">
```python
for event in session.send_stream("Charge R$150 via Pix"):
    if event.type == "assistant_text":
        print(event.content, end="", flush=True)
    elif event.type == "tool_use":
        print(f"\n→ calling {event.name}")
    elif event.type == "done":
        print(f"\nFinal: {event.result.message}")
```
</Tab>
<Tab value="Async">
```python
async for event in session.send_stream("Charge R$150 via Pix"):
    if event.type == "assistant_text":
        print(event.content, end="", flush=True)
    elif event.type == "tool_use":
        print(f"\n→ calling {event.name}")
    elif event.type == "done":
        print(f"\nFinal: {event.result.message}")
```
</Tab>
</Tabs>
</Step>

</Steps>

## Import surfaces

| Class | Use from |
|-------|----------|
| `CodeSpar` | Sync scripts, Jupyter, Flask / Django views |
| `AsyncCodeSpar` | FastAPI, async LangChain, anything on asyncio |
| `Session` / `AsyncSession` | Returned by `create()` — same method names, sync vs async dispatch |

Both clients wrap the same REST API on `api.codespar.dev`. Start with sync, upgrade to async without changing the surrounding code — method names and return types match.

## Common errors

| Error class | Cause | Fix |
|-------------|-------|-----|
| `ConfigError` | Missing or malformed `api_key` | Set `CODESPAR_API_KEY` or pass `CodeSpar(api_key="csk_...")` |
| `NotConnectedError` | Calling a tool before a server finishes connecting | Use `manage_connections={"wait_for_connections": True}` in `create()` |
| `ApiError` | Backend returned non-2xx | Inspect `error.code` and `error.message`; retry or surface to user |
| `StreamError` | SSE stream dropped mid-flight | Reconnect; partial results are in the final event if already emitted |

## Next steps

<NextStepsGrid items={[
  { label: "CONCEPT", title: "Test Mode", description: "Inline mocks, strict-mode behavior, the five tool-result envelopes, and bidirectional OSS parity.", href: "/docs/concepts/test-mode" },
  { label: "CONCEPT", title: "Sessions", description: "Lifecycle, presets, connection management.", href: "/docs/concepts/sessions" },
  { label: "CONCEPT", title: "Tools & Meta-Tools", description: "What each of the 14 meta-tools does.", href: "/docs/concepts/tools" },
  { label: "CONCEPT", title: "Billing", description: "Per-settled-transaction pricing, not per-call.", href: "/docs/concepts/billing" },
  { label: "QUICKSTART", title: "Quickstart (TypeScript)", description: "Bridge a Node service alongside the Python worker.", href: "/docs/quickstart" },
  { label: "COOKBOOK", title: "Pix Payment Agent", description: "The simplest agent, shown in Python too.", href: "/docs/cookbooks/pix-payment-agent" },
]} />
