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.

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"]

# Method 1: Remove resources one at a time
for model in deprecated_models:
    try:
        client.categories.delete_resource(
            resource=model,              
            category="my-custom-models"  
        )
        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")
    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 defined by the start_timestamp parameter, which indicates when the resource becomes valid (defaults to creation time if not specified).

The end of the validity period is determined implicitly:

  • For older versions of a resource, the validity period ends when a newer version's start_timestamp begins
  • For the most recent version of a resource, the validity period extends to the present time (indefinitely until a newer version is created)

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

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.