Once you have initialized AgentPayClient and are able to extract the User API Key from incoming requests, you can charge for the usage of your MCP Server’s features using the consume() method. This is the core mechanism for monetizing your tools with AgentPay.
This guide assumes that you have already successfully extracted the User API Key from the HTTP request. To learn more about that, please refer to our Extracting API Keys guide.

The consume() Method

The agentpay_client.consume() method is called each time you want to record a billable event for a user.

Parameters:

  • api_key (str): Required. The User API Key provided by the client user in their request. This key identifies the user to be charged.
  • amount_cents (int): Required. The cost of the operation in cents. For example, to charge $0.10, you would pass 10. Pass 0 for free operations you want to track.
  • usage_event_id (str): Required. A unique identifier (e.g., a UUID) for this specific consumption event. This is crucial for idempotency. If you make the same consume() call with the same usage_event_id multiple times (e.g., due to a retry), AgentPay will only process and charge for the event once.

Returns:

The consume() method returns a ConsumptionResult object with the following attributes:
  • success (bool): True if the consumption was successful and the charge was applied. False otherwise.
  • error_message (str | None): If success is False, this provides a human-readable message describing the error (e.g., “Insufficient funds,” “Invalid API Key”).
  • error_code (str | None): If success is False, this provides a short error code (e.g., INSUFFICIENT_FUNDS, INVALID_API_KEY) that can be used for programmatic error handling.

Flexible Pricing Strategies

The consume() method gives you flexibility in how you price your tools:

Flat Rate per Feature

@mcp.tool()
async def simple_tool(param1: str) -> str:
    """A tool that always costs the same amount."""
    user_api_key = api_key_context.get()
    if not user_api_key:
        return "Error: API Key missing"

    # Always charge 10 cents for this tool
    COST_CENTS = 10
    usage_id = str(uuid.uuid4())
    consume_result = agentpay_client.consume(
        api_key=user_api_key,
        amount_cents=COST_CENTS,
        usage_event_id=usage_id
    )
    
    if not consume_result.success:
        return f"Error: {consume_result.error_message}"
    
    return f"Tool executed with parameter: {param1}"

Dynamic Pricing

@mcp.tool()
async def text_generator(prompt: str) -> str:
    """A tool that charges based on input length."""
    user_api_key = api_key_context.get()
    if not user_api_key:
        return "Error: API Key missing"

    # Calculate cost based on input length
    BASE_COST = 5  # 5 cents base cost
    COST_PER_CHAR = 0.01  # 0.01 cents per character
    total_cost = BASE_COST + int(len(prompt) * COST_PER_CHAR)
    
    usage_id = str(uuid.uuid4())
    consume_result = agentpay_client.consume(
        api_key=user_api_key,
        amount_cents=total_cost,
        usage_event_id=usage_id
    )
    
    if not consume_result.success:
        return f"Error: {consume_result.error_message}"
    
    return f"Generated text for prompt: {prompt}"

Free Usage Tracking

@mcp.tool()
async def free_tool(param1: str) -> str:
    """A free tool that still tracks usage."""
    user_api_key = api_key_context.get()
    if not user_api_key:
        return "Error: API Key missing"

    # Track usage but don't charge
    usage_id = str(uuid.uuid4())
    consume_result = agentpay_client.consume(
        api_key=user_api_key,
        amount_cents=0,  # Free operation
        usage_event_id=usage_id
    )
    
    if not consume_result.success:
        return f"Error: {consume_result.error_message}"
    
    return f"Free tool executed with parameter: {param1}"
If a user has any free credits remaining with your Service, those will be consumed first before charging their balance. These free credits are tracked separately from your earnings.

Handling Consumption Failures

AgentPay is designed with developer experience in mind. The system automatically handles complex payment scenarios like outstanding payments, maintains clear API key states, and provides automatic recovery when users add funds. This means you can focus on building your tools while AgentPay handles the payment complexities.
The consume() method first validates the API Key, then attempts to charge the user. This means it can fail for two types of reasons:

API Key Validation Failures

If the API Key is invalid, the consumption will fail with one of these reasons:
  • "invalid_key": The key is not recognized or has been revoked/deactivated.
  • "outstanding_payment": The key is associated with an account that has an unpaid consumption attempt. This happens when a previous consume() call failed due to insufficient funds.
  • "insufficient_balance": The key is valid but the associated account has no remaining balance (including free credits).

Consumption Failures

If the API Key is valid but the consumption fails due to insufficient funds:
  1. AgentPay will:
    • Log a pending payment for the full amount
    • Mark the API Key as invalid with reason "outstanding_payment"
    • Display a negative balance in the user’s account
  2. When the user adds funds to their account:
    • The outstanding payment will be automatically paid first
    • The API Key will be reactivated once the payment is complete
    • Any remaining balance will be available for future consumption

Best Practices

  • Validate First: By validating the API Key before running expensive business logic (using validate_api_key()), you’ll naturally catch invalid keys (including those with outstanding payments) early.
  • Handle Gracefully: If consume() fails, inform the user they need to add funds to their AgentPay account. The system will automatically handle reactivating their key once they’ve paid their outstanding balance.
  • Track Usage: Even for free operations, use consume() with amount_cents=0 to track usage patterns.

Idempotency

The usage_event_id is vital. If a network error occurs after you’ve called consume() but before you receive a response, or if your server process restarts unexpectedly, you might retry the operation. By sending the same usage_event_id, you ensure the user is not charged multiple times for the same logical operation.
  • Always generate a new usage_event_id for each distinct user request or billable action.
  • Persist this ID or be able to regenerate it reliably if you need to retry the consume() call for that specific action.

Next Steps