Guides

Python SDK Categories and Resources API

Overview

This guide explains how to manage Categories and Resources using the Pay-i Python SDK. For a conceptual understanding of what Categories and Resources are and how they fit into the Pay-i platform, please refer to the Categories and Resources Concepts page.

For a detailed API reference with complete parameter and return type information, see the Python SDK Categories and Resources API Reference.

When to Use Categories and Resources

You'll work with categories and resources in the Pay-i Python SDK for several important purposes:

  1. Exploring Available Models: Discover which AI models Pay-i currently supports and their pricing
  2. Creating Custom Resources: Track usage of custom AI models or services not natively supported by Pay-i
  3. Setting Up Pricing: Define pricing for custom or enterprise-negotiated services
  4. Cost Management: View and understand the details of resources you're using in your applications

Common Workflows

Working with the Pay-i Categories and Resources API involves several common patterns for discovering, creating, and managing AI model resources. This section walks you through these workflows with practical code examples.

The examples below demonstrate how to:

  1. Discover available AI models and resources
  2. Create custom categories and resources
  3. Retrieve and manage resources
  4. Delete resources when no longer needed

Note: The examples in this guide use the Python SDK's client objects (Payi and AsyncPayi), which provide a resource-based interface to the Pay-i API. For details on client initialization and configuration, see the Pay-i Client Initialization guide.

Discovering Available AI Models and Resources

When you're planning your AI implementation, you'll often want to explore what models are available and understand their pricing structure:

from payi import Payi

# Initialize the Pay-i client
client = Payi()  # API key will be loaded from PAYI_API_KEY environment variable

# Step 1: List available categories
print("Available Categories:")
categories = client.categories.list()
for category in categories:
    print(f"Category: {category.category}")
    
# Step 2: For a specific category, list all resources
# IMPORTANT: Use client.categories.list_resources() to list all resources in a category
# NOT client.categories.resources.list() which is for listing all versions of a specific resource
print("\nListing all resources in the OpenAI category:")
openai_resources = client.categories.list_resources(category="system.openai")
for resource in openai_resources:
    print(f"  Resource: {resource.resource}")

# Step 3: Find and print details for a specific resource
print("\nChecking details for OpenAI 'gpt-4o' resource:")
# Find the gpt-4o resource in the list
gpt4o_resource = next((r for r in openai_resources if r.resource == "gpt-4o"), None)

if gpt4o_resource:
    print(f"  Resource: {gpt4o_resource.resource}")
    
    # Step 4: Print pricing details
    print(f"    Input Price: ${gpt4o_resource.units['text'].input_price} per token")
    print(f"    Output Price: ${gpt4o_resource.units['text'].output_price} per token")
    print(f"    Max Input Tokens: {gpt4o_resource.max_input_units}")
else:
    print("  Resource 'gpt-4o' not found in the OpenAI category.")

# Note: If you need to list all versions of a specific resource instead,
# use client.categories.resources.list():
# resource_versions = client.categories.resources.list(
#     resource="gpt-4o",
#     category="system.openai"
# )

Implementing Custom Resource Tracking

If you're using AI models or services that aren't managed by Pay-i (like custom-trained models, vector databases, or enterprise services), you'll need to create custom resources to track their usage:

from datetime import datetime, timezone
from payi import Payi

client = Payi()

# Step 1: Create a custom category and resource for a vector database service
# Note: In production, you might want to check if the resource exists first
# or handle potential exceptions if the resource already exists.
response = client.categories.resources.create(
    category="vector-db",           # Your custom category
    resource="qdrant-embeddings",   # Your custom resource
    units={
        "text": {
            "input_price": 0.0000025,  # Price per embedding token
            "output_price": 0.0000000  # No output cost for vector search
        }
    },
    max_input_units=10000,          # Max tokens per request
    max_output_units=0,
    # IMPORTANT: If you plan to track historical data for this resource,
    # set a start_timestamp that predates your oldest events
    # Otherwise, events with timestamps before the resource creation will be rejected
    start_timestamp=datetime.now(timezone.utc)  # Defaults to now if tracking current events only
)
print(f"Created resource with ID: {response.resource_id}")
    
# Step 2: Now track usage of this custom resource in your application
def track_vector_db_usage(query_text_length):
    response = client.ingest.units(
        category="vector-db",
        resource="qdrant-embeddings",
        units={
            "text": {
                "input": query_text_length,  # Number of tokens processed
                "output": 0
            }
        },
        http_status_code=200,
        end_to_end_latency_ms=125,
        user_id="customer_abc123"  # Optional user attribution
    )
    return response

# Example usage in your application
track_vector_db_usage(query_text_length=450)

Setting Up Enterprise-Negotiated Pricing

If your organization has negotiated special pricing with AI providers, you can create custom resources with your negotiated rates:

from payi import Payi
from datetime import datetime, timezone

client = Payi()

# Set up your enterprise-negotiated pricing for OpenAI models
# This is useful when you have special pricing different from Pay-i's standard rates
enterprise_pricing = client.categories.resources.create(
    category="enterprise-openai",                # Custom category for your enterprise
    resource="gpt-4-enterprise",                 # Custom resource name
    units={
        "text": {
            "input_price": 0.000005,             # Your negotiated rates
            "output_price": 0.000015
        }
    },
    max_input_units=128000,
    max_output_units=128000,
    # If you plan to backfill historical data, set this to before your oldest events
    start_timestamp="2024-01-01T00:00:00Z"  # Valid from Jan 1, 2024
)

Managing Resource Lifecycle

As your AI infrastructure evolves, you may need to update or remove resources that are no longer needed. When resources are deleted in Pay-i, they are "soft deleted" - meaning:

  1. The resource will no longer appear in list operations
  2. You cannot create new events using that resource
  3. Historical data using that resource remains valid and accessible
  4. All previously ingested events on the deleted resources will continue to be valid in reporting and analytics

This approach ensures your historical data integrity is maintained while allowing you to manage your current resource catalog effectively:

from payi import Payi

client = Payi()

# Scenario: Removing deprecated or unused custom resources
deprecated_models = ["old-model-v1", "deprecated-embedding-model"]

# Best practice: Use keyword parameters consistently for clarity and to avoid errors
# This makes code more readable and less prone to parameter ordering mistakes

# Method 1: Remove resources one at a time with proper error handling
for model in deprecated_models:
    try:
        client.categories.delete_resource(
            resource=model,              # Use keyword parameter for all arguments
            category="my-custom-models"  # Use keyword parameter for all arguments
        )
        print(f"Successfully removed {model}")
    except Exception as e:
        print(f"Could not remove {model}: {e}")

# Method 2: Alternative approach - Delete an entire category and all its resources at once
# This is more efficient when you need to remove many resources in the same category
try:
    # This deletes the entire category and all resources it contains
    client.categories.delete(category="my-custom-models")  # Use keyword parameter for clarity
    print("Successfully removed the entire category and all its resources")
except Exception as e:
    print(f"Could not remove category: {e}")

Real-World Example: Building a Multi-Model AI Application

Let's walk through a comprehensive example of setting up and tracking usage for a complete AI application that uses multiple services:

from payi import Payi
from datetime import datetime, timezone

# Initialize the Pay-i client
client = Payi()

# Step 1: Set up custom resources for all AI components in our application
def setup_resources():
    # Create embeddings model resource
    client.categories.resources.create(
        category="company-ai-stack",
        resource="text-embeddings",
        units={"text": {"input_price": 0.0000010, "output_price": 0.0}},
        max_input_units=8000,
        # Set to a date that predates any historical data you might ingest
        start_timestamp="2024-01-01T00:00:00Z"  # Valid from Jan 1, 2024
    )
    
    # Create vector database resource
    client.categories.resources.create(
        category="company-ai-stack",
        resource="vector-search",
        units={"text": {"input_price": 0.00000025, "output_price": 0.0}},
        max_input_units=10000,
        start_timestamp="2024-01-01T00:00:00Z"  # Valid from Jan 1, 2024
    )
    
    # Create custom LLM resource
    client.categories.resources.create(
        category="company-ai-stack",
        resource="rag-completion-model",
        units={"text": {"input_price": 0.000006, "output_price": 0.000012}},
        max_input_units=16000,
        max_output_units=4000,
        start_timestamp="2024-01-01T00:00:00Z"  # Valid from Jan 1, 2024
    )
    
    print("AI resource stack configured successfully")

# Step 2: Create a function to track RAG pipeline usage
def track_rag_query(query_text, context_chunks, response_text, user_id):
    # Track embeddings usage
    client.ingest.units(
        category="company-ai-stack",
        resource="text-embeddings",
        units={"text": {"input": len(query_text) // 4, "output": 0}},  # Approximate tokens
        user_id=user_id
    )
    
    # Track vector search usage
    client.ingest.units(
        category="company-ai-stack",
        resource="vector-search",
        units={"text": {"input": len(query_text) // 4, "output": 0}},
        user_id=user_id
    )
    
    # Track LLM usage
    context_tokens = sum(len(chunk) // 4 for chunk in context_chunks)
    query_tokens = len(query_text) // 4
    response_tokens = len(response_text) // 4
    
    client.ingest.units(
        category="company-ai-stack",
        resource="rag-completion-model",
        units={"text": {
            "input": query_tokens + context_tokens,  # Query + context
            "output": response_tokens                # Generated response
        }},
        user_id=user_id
    )
    
    print(f"Tracked complete RAG pipeline usage for user {user_id}")

# Usage in application
def main():
    # Setup resources - safe to call every time as create operations are idempotent
    # (If resources already exist, the create calls are handled gracefully)
    setup_resources()
    
    # Example RAG query tracking
    query = "What are the key features of quantum computing?"
    context_chunks = [
        "Quantum computing uses quantum bits or qubits which can exist in multiple states simultaneously.",
        "Unlike classical bits that can be either 0 or 1, qubits leverage superposition and entanglement.",
        "This allows quantum computers to process complex calculations exponentially faster than classical computers for certain problems."
    ]
    response = "Quantum computing's key features include qubits (which leverage superposition to exist in multiple states simultaneously), entanglement (allowing qubits to be correlated with each other), and quantum interference. These properties enable quantum computers to solve certain complex problems exponentially faster than classical computers, particularly in areas like cryptography, optimization, simulation of quantum systems, and certain types of machine learning tasks."
    
    track_rag_query(query, context_chunks, response, user_id="user_12345")

if __name__ == "__main__":
    main()

Resource Timestamps and Historical Data

Pay-i resources have a temporal dimension that's critical for historical data tracking:

Understanding Resource Validity Periods

Each resource in Pay-i has a validity period:

  1. start_timestamp: When the resource becomes valid (defaults to creation time if not specified)
  2. end_timestamp: When the resource stops being valid (optional, null means "forever")

When submitting events via client.ingest.units(), Pay-i validates that the resource existed at the event's timestamp. If you try to ingest an event with a timestamp before the resource's start_timestamp, you'll receive a 404 category_resource_not_found error.

Setting start_timestamp for Historical Data

When creating resources that will be used for tracking historical data:

# To allow tracking historical data from January 1, 2024 onward:
client.categories.resources.create(
    category="custom.my_company",
    resource="proprietary-llm-v1",
    units={"text": {"input_price": 0.0001, "output_price": 0.0005}},
    # Make the resource valid from before your oldest events
    start_timestamp="2024-01-01T00:00:00Z"  # Valid from Jan 1, 2024
)

The start_timestamp parameter is especially important in these scenarios:

  1. Historical Data Backfill: When importing logs or data from the past
  2. Third-party Integration: When connecting existing analytics systems with historical events
  3. Data Migration: When moving from another tracking system to Pay-i

Best Practices

When working with categories and resources in the Pay-i Python SDK, consider these best practices:

  1. Always Create: You can create resources without worrying if they exist beforehand. The API is designed to handle creation requests idempotently.

  2. Use Descriptive Names: Choose meaningful category and resource names that clearly indicate their purpose.

  3. Consider Use vs Cost Tracking: For custom resources, you have flexibility in how you track usage. While setting accurate prices ensures correct cost calculations, many customers use custom resources with zero costs (input_price: 0, output_price: 0) to track usage patterns independently from cost tracking. This approach is particularly useful when you want to monitor utilization without tying it directly to financial metrics.

  4. Track Complete Workflows: When using multiple AI services together (like in RAG), track all components of the workflow to get a complete picture of costs.

  5. Version Resources: When updating pricing or specifications, consider creating new versioned resources rather than modifying existing ones to maintain historical data.

  6. Set Appropriate start_timestamp: When creating resources for historical data tracking, always set a start_timestamp that predates your oldest events to avoid validation errors.

Pagination in List Methods

When using any list() method in the SDK (such as client.categories.list() or client.categories.list_resources()), pagination is automatically handled for you:

# Automatically iterates through all pages of results
for category in client.categories.list():
    print(f"Category: {category.category}")
    
# The same automatic pagination works for resources
for resource in client.categories.list_resources(category="system.openai"):
    print(f"Resource: {resource.resource}")

You don't need to manually handle pagination - the SDK takes care of making multiple API calls as needed when you iterate through the results. This makes it easy to work with large collections of categories and resources without worrying about pagination implementation details.

For advanced usage, including cursor and sorting parameters, see the Pagination Parameters documentation.

API Reference

For detailed information on all the methods, parameters, and response types provided by the client.categories resource, please refer to the Python SDK Categories and Resources API Reference.

The reference documentation includes:

  • Complete method signatures with all parameters
  • Return type structures for all response types
  • Detailed explanations of parameter behavior
  • REST API endpoint mappings
  • Examples for each method

This separate reference guide complements the workflow examples provided in this document, offering a more technical and comprehensive view of the categories and resources API.