> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metrifox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> Official **Python SDK** for the Metrifox platform. Build and scale usage-based SaaS applications with comprehensive tools for customer management, usage tracking, access control, and billing.

## Installation

```bash theme={null}
pip install metrifox-sdk
```

***

## Quick Start

### Configuration

```python theme={null}
from metrifox_sdk import MetrifoxClient

# Option 1: Initialize with API key
client = MetrifoxClient(api_key="your_api_key")

# Option 2: Use environment variable
# Set METRIFOX_API_KEY=your_api_key in your environment
client = MetrifoxClient()

# Option 3: Use the init function
from metrifox_sdk import init
client = init({"api_key": "your_api_key"})
```

<Note>
  Get your API key from **Settings → API Keys** in your Metrifox dashboard.
</Note>

***

## Customer Management

### Create a Customer

```python theme={null}
# Individual customer
customer = client.customers.create({
    "customer_key": "user_12345",  # Required: unique identifier
    "customer_type": "INDIVIDUAL",  # Required: "INDIVIDUAL" or "BUSINESS"
    "primary_email": "john.doe@example.com",  # Required
    "first_name": "John",
    "last_name": "Doe",
    "currency": "USD"
})

# Business customer
customer = client.customers.create({
    "customer_key": "company_abc123",
    "customer_type": "BUSINESS",
    "primary_email": "contact@acmecorp.com",
    "legal_name": "ACME Corporation LLC",
    "display_name": "ACME Corp",
    "website_url": "https://acmecorp.com"
})
```

### Type-Safe Dataclasses

For better type safety and IDE support:

```python theme={null}
from metrifox_sdk import CustomerCreateRequest

customer_request = CustomerCreateRequest(
    customer_key="user_12345",
    customer_type="INDIVIDUAL",
    primary_email="john.doe@example.com",
    first_name="John",
    last_name="Doe",
    currency="USD"
)

customer = client.customers.create(customer_request)
```

### Update a Customer

```python theme={null}
response = client.customers.update("user_12345", {
    "primary_email": "newemail@example.com",
    "first_name": "Jane",
    "currency": "EUR"
})
```

<Note>
  The `customer_key` cannot be changed after creation.
</Note>

### Get Customer Data

```python theme={null}
# Get basic customer data
customer = client.customers.get("customer_123")

# Get detailed customer information (includes usage stats, billing info)
details = client.customers.get_details("customer_123")

# List customers with pagination
customers = client.customers.list({
    "page": 1,
    "per_page": 50
})

# List with filters
customers = client.customers.list({
    "search_term": "john@example.com",
    "customer_type": "INDIVIDUAL",
    "date_created": "2025-09-01"
})

# Check if customer has active subscription
is_active = client.customers.has_active_subscription("customer_123")
```

### Delete a Customer

```python theme={null}
response = client.customers.delete("customer_123")
```

### Archive / Unarchive a Customer

```python theme={null}
# Archive a customer (preserves their full history)
response = client.customers.archive("customer_123")
print(response["data"]["archived_at"])

# Restore an archived customer
response = client.customers.unarchive("customer_123")
```

### Bulk CSV Upload

```python theme={null}
result = client.customers.upload_csv("/path/to/customers.csv")

print(f"Total: {result['data']['total_customers']}")
print(f"Successful: {result['data']['successful_upload_count']}")
print(f"Failed: {result['data']['failed_upload_count']}")
```

***

## Usage Tracking & Access Control

### Check Feature Access

```python theme={null}
access = client.usages.check_access({
    "feature_key": "premium_feature",
    "customer_key": "customer_123",
    "quantity": 1  # how many units to check access for (defaults to 1)
})

if access['data']['can_access']:
    print(f"Access granted. Balance: {access['data']['balance']}")
else:
    print("Access denied - upgrade needed")
```

### Type-Safe Access Checks

```python theme={null}
from metrifox_sdk import AccessCheckRequest

access_request = AccessCheckRequest(
    feature_key="premium_feature",
    customer_key="customer_123",
    quantity=1,  # how many units to check access for (defaults to 1)
)

access = client.usages.check_access(access_request)
```

### Record Usage Events

```python theme={null}
import time

# Simple usage recording
response = client.usages.record_usage({
    "customer_key": "customer_123",
    "event_name": "api_call",
    "event_id": "evt_unique_123",  # Required for idempotency
    "quantity": 1
})

# Advanced usage with metadata
response = client.usages.record_usage({
    "customer_key": "customer_123",
    "feature_key": "premium_feature",
    "event_id": "evt_unique_456",
    "quantity": 5,
    "credit_used": 25,
    "timestamp": int(time.time() * 1000),  # milliseconds
    "metadata": {
        "source": "web_app",
        "session_id": "sess_xyz"
    }
})
```

### Type-Safe Usage Events

```python theme={null}
from metrifox_sdk import UsageEventRequest
import time

usage_request = UsageEventRequest(
    customer_key="customer_123",
    event_name="premium_feature",
    event_id="evt_unique_123",
    quantity=1,
    timestamp=int(time.time() * 1000),
    metadata={"source": "mobile_app"}
)

response = client.usages.record_usage(usage_request)
```

### List Usage Events

Retrieve usage events with optional filters and pagination:

```python theme={null}
events = client.usages.list_events(
    customer_key="customer_123",
    feature_key="premium_feature",
    page=1,
    per_page=25
)

for event in events['data']:
    print(f"{event['feature_key']}: qty={event['quantity']} at {event['timestamp']}")

print(f"Page {events['meta']['current_page']} of {events['meta']['total_pages']}")
```

### Compute Quantity Price

Compute the price for a given quantity of a feature for a customer, based on their plan. Useful for previewing upgrade costs or showing customers the cost of additional usage before they commit.

```python theme={null}
price = client.usages.quantity_price(
    customer_key="customer_123",
    feature_key="feature_interview_booking",
    quantity=500
)

print(f"{price['data']['price']} {price['data']['unit']}")

# For tiered pricing, inspect the per-tier breakdown
for tier in price['data'].get('applied_tiers', []):
    print(
        f"  Tier {tier['first_unit']}-{tier['last_unit']}: "
        f"{tier['units_consumed']} units -> {tier['tier_price']}"
    )
```

<Note>
  Only available to tenants whose plan includes the finance API feature.
</Note>

### Complete Usage Example

```python theme={null}
def use_feature(customer_key, feature_key, event_name):
    # 1. Check if customer has access
    access = client.usages.check_access({
        "feature_key": feature_key,
        "customer_key": customer_key
    })

    if access['data']['can_access']:
        # 2. Perform the feature logic
        result = perform_feature_logic()

        # 3. Record usage after completion
        client.usages.record_usage({
            "customer_key": customer_key,
            "event_name": event_name,
            "event_id": f"evt_{result['transaction_id']}",
            "quantity": result.get('units_used', 1)
        })

        return {"success": True, "data": result}
    else:
        return {
            "success": False,
            "error": "Quota exceeded",
            "balance": access['data']['balance']
        }
```

***

## Checkout & Billing

### Generate Checkout URL

```python theme={null}
# Basic checkout URL
checkout_url = client.checkout.url({
    "offering_key": "premium_plan"
})

# With billing interval
checkout_url = client.checkout.url({
    "offering_key": "premium_plan",
    "billing_interval": "monthly"
})

# With customer key for pre-filled checkout
checkout_url = client.checkout.url({
    "offering_key": "premium_plan",
    "billing_interval": "monthly",
    "customer_key": "customer_123"
})
```

### Type-Safe Checkout

```python theme={null}
from metrifox_sdk import CheckoutConfig

config = CheckoutConfig(
    offering_key="premium_plan",
    billing_interval="monthly",
    customer_key="customer_123"
)

checkout_url = client.checkout.url(config)
```

### Card Collection URL

Generate a hosted URL for collecting a payment method against an existing subscription or order. Useful for trial-to-paid conversions and re-collecting card details after expiry.

```python theme={null}
# For a subscription
url = client.checkout.card_collection_url(subscription_id="sub_uuid_123")

# For an order
url = client.checkout.card_collection_url(order_id="order_uuid_456")
```

***

## Wallets & Credit Allocations

```python theme={null}
# List a customer's credit wallets
wallets = client.wallets.list("customer_123")
for w in wallets['data']:
    print(f"{w['name']}: {w['balance']} {w['credit_unit_plural']}")

# List allocations for a wallet (optionally filter by status)
allocations = client.wallets.list_credit_allocations("wallet_uuid_123")
active = client.wallets.list_credit_allocations("wallet_uuid_123", status="active")

# Get a single allocation with its transaction history
alloc = client.wallets.get_credit_allocation("alloc_uuid_123")
for txn in alloc['data']['transactions']:
    print(f"  {txn['amount']} at {txn['created_at']}")
```

***

## Error Handling

```python theme={null}
from metrifox_sdk import APIError, ConfigurationError

try:
    customer = client.customers.get("nonexistent_customer")
except ConfigurationError as e:
    print(f"Configuration error: {e}")
except APIError as e:
    print(f"API error: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Response: {e.response_body}")
```

***

## Framework Integration

### Django

```python theme={null}
# settings.py
from metrifox_sdk import MetrifoxClient
import os

METRIFOX_CLIENT = MetrifoxClient(api_key=os.getenv('METRIFOX_API_KEY'))

# views.py
from django.conf import settings
from django.http import JsonResponse

def premium_feature_view(request):
    customer_key = request.user.customer_key

    access = settings.METRIFOX_CLIENT.usages.check_access({
        "feature_key": "premium_feature",
        "customer_key": customer_key
    })

    if not access['data']['can_access']:
        return JsonResponse({
            "error": "Access denied",
            "balance": access['data']['balance']
        }, status=403)

    # Process the feature...

    settings.METRIFOX_CLIENT.usages.record_usage({
        "customer_key": customer_key,
        "event_name": "premium_feature_used",
        "event_id": f"evt_{request.id}"
    })

    return JsonResponse({"success": True})
```

### Flask

```python theme={null}
from flask import Flask, jsonify, request
from metrifox_sdk import MetrifoxClient
import os

app = Flask(__name__)
metrifox_client = MetrifoxClient(api_key=os.getenv('METRIFOX_API_KEY'))

@app.route('/api/premium/<customer_id>', methods=['GET'])
def premium_endpoint(customer_id):
    # Check access
    access = metrifox_client.usages.check_access({
        "feature_key": "premium_api",
        "customer_key": customer_id
    })

    if not access['data']['can_access']:
        return jsonify({"error": "Access denied"}), 403

    # Process request...

    # Record usage
    metrifox_client.usages.record_usage({
        "customer_key": customer_id,
        "event_name": "premium_api_call",
        "event_id": f"evt_{request.headers.get('X-Request-ID')}"
    })

    return jsonify({"data": "premium content"})
```

### FastAPI

```python theme={null}
from fastapi import FastAPI, HTTPException
from metrifox_sdk import MetrifoxClient
import os

app = FastAPI()
metrifox = MetrifoxClient(api_key=os.getenv('METRIFOX_API_KEY'))

@app.get("/api/feature/{customer_id}")
async def get_premium_feature(customer_id: str):
    access = metrifox.usages.check_access({
        "feature_key": "premium_feature",
        "customer_key": customer_id
    })

    if not access['data']['can_access']:
        raise HTTPException(status_code=403, detail="Access denied")

    # Process...

    metrifox.usages.record_usage({
        "customer_key": customer_id,
        "event_name": "feature_accessed",
        "event_id": f"evt_{customer_id}_{int(time.time())}"
    })

    return {"status": "success"}
```

***

## API Reference

### Client Methods

**Initialization:**

* `MetrifoxClient(api_key, base_url, web_app_base_url, meter_service_base_url)` - Initialize the client
* `init(config)` - Convenience function to initialize the client

**Customers Module (`client.customers`):**

* `create(request)` - Create a customer
* `update(customer_key, request)` - Update a customer
* `get(customer_key)` - Get a customer
* `get_details(customer_key)` - Get detailed customer information
* `list(params)` - List customers with pagination and filters
* `delete(customer_key)` - Delete a customer
* `archive(customer_key)` - Archive a customer
* `unarchive(customer_key)` - Restore an archived customer
* `has_active_subscription(customer_key)` - Check for active subscription
* `upload_csv(file_path)` - Upload customers via CSV
* `bulk_create(customers)` - Create multiple customers in one call

**Usages Module (`client.usages`):**

* `check_access(request)` - Check feature access
* `record_usage(request)` - Record a usage event
* `list_events(customer_key, feature_key, page, per_page)` - List recorded usage events
* `quantity_price(customer_key, feature_key, quantity)` - Compute the price of a usage quantity (requires finance API feature)

**Checkout Module (`client.checkout`):**

* `url(config)` - Generate a checkout URL
* `card_collection_url(subscription_id, order_id)` - Generate a hosted card-collection URL

**Wallets Module (`client.wallets`):**

* `list(customer_key)` - List a customer's credit wallets
* `list_credit_allocations(wallet_id, status)` - List credit allocations for a wallet
* `get_credit_allocation(allocation_id)` - Get a single allocation with transaction history

### Type Definitions

All type definitions are available for import:

```python theme={null}
from metrifox_sdk import (
    CustomerCreateRequest,
    CustomerUpdateRequest,
    CustomerListRequest,
    UsageEventRequest,
    AccessCheckRequest,
    CheckoutConfig
)
```

***

## Configuration

### Environment Variables

* `METRIFOX_API_KEY` - Your Metrifox API key
* `METRIFOX_METER_SERVICE_BASE_URL` - Optional override for the meter service URL

### Custom URLs

```python theme={null}
client = MetrifoxClient(
    api_key="your_api_key",
    base_url="https://custom-api.metrifox.com/api/v1/",
    web_app_base_url="https://custom-app.metrifox.com",
    meter_service_base_url="https://custom-meter.metrifox.com/"
)
```

### Default URLs

* **Production API:** `https://api.metrifox.com/api/v1/`
* **Meter Service:** `https://api-meter.metrifox.com/`
* **Web App:** `https://app.metrifox.com`

***

## Requirements

* Python 3.8+
* requests >= 2.31.0

***

## Support

* **Email:** [support@metrifox.com](mailto:support@metrifox.com)
* **Documentation:** [https://docs.metrifox.com](https://docs.metrifox.com)
* **GitHub:** [https://github.com/metrifox/metrifox-python](https://github.com/metrifox/metrifox-python)
* **Discord** [https://discord.gg/GGMHDDBdw](https://discord.gg/GGMHDDBdw)

<Note>
  The Python SDK follows Pythonic patterns with type hints, dataclasses, and comprehensive error handling for a great developer experience.
</Note>
