Metadata-Version: 2.4
Name: merge-gateway-python
Version: 0.4.0
Summary: Python SDK for the Merge Gateway API
License-Expression: MIT
Project-URL: Homepage, https://github.com/merge-api/merge-gateway-python-sdk
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.25.0
Requires-Dist: pydantic>=2.0
Dynamic: license-file

# merge-gateway-python

Python SDK for the [Merge Gateway](https://merge.dev) API.

## Installation

```bash
pip install merge-gateway-python

# Local development install
pip install -e ./
```

## Quick start

```python
from merge_gateway import MergeGateway

client = MergeGateway(
    api_key="sk-...",
    base_url="https://api-gateway-develop.merge.dev/v1",  # default
)
```

## Usage

### Responses

```python
# Non-streaming
response = client.responses.create(
    model="openai/gpt-4o",
    input=[{"type": "message", "role": "user", "content": "Tell me a bedtime story about a otter."}],
)
print(response.output[0].content)

# Streaming
stream = client.responses.create(
    model="openai/gpt-4o",
    input=[{"type": "message", "role": "user", "content": "Hello"}],
    stream=True,
)
for event in stream:
    print(event)
```

### Models

```python
# List models (with optional pagination)
models = client.models.list()
for m in models.data:
    print(m.id, m.display_name)

# Filter by provider
openai_models = client.models.list(provider="openai")

# Retrieve a specific model
model = client.models.retrieve("openai/gpt-4o")
```

### Embeddings

```python
result = client.embeddings.create(
    model="openai/text-embedding-3-small",
    input="The food was delicious",
    encoding_format="float",
)
print(result.data[0].embedding)
```

### Embedded Routing (customers)

Provision each of your end customers as a Customer with its own routing policies, provider keys, and budget, then scope LLM requests to it with the `customer` field.

```python
# Provision a customer
customer = client.customers.create(name="Acme", origin_id="acme-corp")

client.customers.routing_policies.create(
    customer.id,
    name="workflow-a",
    strategy="PRIORITY",
    is_default=True,
    priority_order=[
        {"model": "openai/gpt-5.5", "priority": 1},
        {"model": "anthropic/claude-opus-4-8", "priority": 2},
    ],
)

client.customers.keys.upsert(customer.id, vendor="openai", api_key="sk-...")

client.customers.budgets.create(
    customer.id, spending_limit=50, reset_period="MONTHLY", spending_limit_type="HARD"
)

# Scope a request to the customer (omit `model` to route via its default policy)
response = client.responses.create(input="Hello!", customer=customer.id)

# Per-customer spend report
usage = client.customers.usage(customer.id, start="2026-07-01", end="2026-07-31")
print(usage.customer_byok_spend, usage.organization_byok_spend, usage.merge_spend)
```

### Structured output

Request schema-constrained JSON with `response_format`. `strict` is
tri-state: omit it for the provider default, pass `strict=False` for schemas
with optional fields or unions (OpenAI-style strict mode rejects those). If
the routed model can't honor the schema, the gateway returns a clear
`400 unsupported_params` — never silent best-effort JSON.

```python
from typing import Optional

from pydantic import BaseModel

from merge_gateway import response_format_from_model


class Person(BaseModel):
    name: str
    age: Optional[int] = None


response = client.responses.create(
    model="openai/gpt-4o",
    input="Invent a person",
    response_format=response_format_from_model("person", Person, strict=False),
)
```

Or hand-build the payload (typed models in `merge_gateway.types` or a plain dict):

```python
response = client.responses.create(
    model="openai/gpt-4o",
    input="Invent a person",
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "schema": {
                "type": "object",
                "properties": {"name": {"type": "string"}},
                "required": ["name"],
                "additionalProperties": False,
            },
            "strict": True,
        },
    },
)
```

### Error handling

```python
from merge_gateway import AuthenticationError, RateLimitError

try:
    response = client.responses.create(model="openai/gpt-4o", input="Hi")
except AuthenticationError:
    print("Check your API key")
except RateLimitError:
    print("Slow down!")
```

## Publishing to PyPI

```bash
python -m build
twine upload dist/*
```
