Menu

Routes Overview

Routes are the core of ApiCharge's configuration, defining which APIs are monetized and how they're accessed. Each route maps client requests to backend services and provides the foundation for monetization through ApiCharge Subscriptions.

Note: ApiCharge routes are built on top of the YARP reverse proxy framework, with additional monetization capabilities.

Route Configuration

Routes are configured in the ApiChargeProxy section of appsettings.json, which contains both routes and clusters:

{
  "ApiChargeProxy": {
    "Routes": {
      "basic-route": {
        "ClusterId": "api-cluster",
        "Match": {
          "Path": "/api/basic/{**catch-all}"
        }
      },
      "premium-route": {
        "ClusterId": "api-cluster",
        "Match": {
          "Path": "/api/premium/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "api-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://api-backend.example.com"
          }
        }
      }
    }
  }
}

Route Structure

Each route has the following components:

Route ID

The route ID is a unique identifier for the route, used in quotes and access tokens. In the example above, basic-route and premium-route are the route IDs.

ClusterId

The ClusterId links the route to its destination(s) defined in the Clusters section. A cluster represents one or more backend destinations that the request will be forwarded to.

Match

The Match object defines the criteria for matching incoming requests to this route. It supports three main matching types:

For detailed information on match configuration, see the Route Matching page.

Metadata (Optional)

Routes can include optional metadata for additional information:

"basic-route": {
  "ClusterId": "basic-api-cluster",
  "Match": {
    "Path": "/api/basic/{**catch-all}"
  },
  "Metadata": {
    "Description": "Basic API with limited features",
    "Category": "Public",
    "ApiVersion": "v1"
  }
}

Metadata is useful for documentation and organization but doesn't affect the route's behavior.

Clusters Configuration

Clusters define where requests should be forwarded once a route is matched. Each cluster can have one or more destinations:

"Clusters": {
  "basic-api-cluster": {
    "Destinations": {
      "destination1": {
        "Address": "https://api-backend-1.example.com/basic"
      },
      "destination2": {
        "Address": "https://api-backend-2.example.com/basic"
      }
    },
    "LoadBalancingPolicy": "RoundRobin"
  }
}

Destinations

Each destination includes:

LoadBalancingPolicy

When multiple destinations are specified, you can configure how requests are distributed:

Configuring Routes for Monetization

To monetize a route, you must define quotes for it in the ApiChargeQuotes section:

{
  "ApiChargeQuotes": {
    "Quotes": [
      {
        "RouteId": "basic-route",
        "Duration": 3600,
        "MicroUnitPrice": 100000,
        "RateLimiters": [
          {
            "$type": "CallCount",
            "Count": 100
          }
        ]
      },
      {
        "RouteId": "basic-route",
        "Duration": 86400,
        "MicroUnitPrice": 500000,
        "RateLimiters": [
          {
            "$type": "CallCount",
            "Count": 1000
          }
        ]
      }
    ]
  }
}

This configuration defines two different quotes for the basic-route:

  1. A 1-hour (3600 seconds) access with 100 calls for 0.01 XLM (100,000 micro units)
  2. A 24-hour (86400 seconds) access with 1000 calls for 0.05 XLM (500,000 micro units)

Clients can choose which quote best suits their needs when purchasing access.

Protocol Support

ApiCharge routes support a wide range of protocols:

ApiCharge automatically applies rate limiting to all these protocols, with protocol-specific handling for streaming connections.

Note: The middleware provides a "probe" utility that can be used to identify route quotes by route without the client having to do its own route path matching. This is useful for scenarios when a StreamCharge end user app is directed to a specific link (such as a premium article) and the app needs to first purchase the right subscription for that route using the quote. Simply send a request to the desired route with the apicharge header set to probe, and the middleware will return the associated route ID that can be used to purchase the appropriate subscription. There are other methods such as using header transforms as described in Transforms, but this probe utility offers a simpler approach.

WebSocket and SSE Example

Configure a route for WebSockets and SSE in the same way as HTTP routes:

"websocket-route": {
  "ClusterId": "websocket-cluster",
  "Match": {
    "Path": "/api/ws/{**catch-all}"
  }
}

For these streaming protocols, you'll want to consider using the StreamRate limiters to control bandwidth:

{
  "RouteId": "websocket-route",
  "Duration": 3600,
  "MicroUnitPrice": 200000,
  "RateLimiters": [
    {
      "$type": "StreamRateDownload",
      "BytesPerSecond": 51200,
      "WindowDurationInSeconds": 60
    },
    {
      "$type": "StreamRateUpload",
      "BytesPerSecond": 10240,
      "WindowDurationInSeconds": 60
    }
  ]
}

This configuration allows for 50KB/s download and 10KB/s upload speeds for WebSocket or SSE connections.

Example Route Configurations

Here are some practical examples of route configurations for different scenarios:

REST API with Different Service Tiers

"ApiChargeProxy": {
  "Routes": {
    "api-basic": {
      "ClusterId": "api-backend",
      "Match": {
        "Path": "/api/basic/{**catch-all}"
      }
    },
    "api-premium": {
      "ClusterId": "api-backend",
      "Match": {
        "Path": "/api/premium/{**catch-all}"
      }
    }
  },
  "Clusters": {
    "api-backend": {
      "Destinations": {
        "api-server": {
          "Address": "https://internal-api.example.com/"
        }
      }
    }
  }
}

"ApiChargeQuotes": {
  "Quotes": [
    {
      "RouteId": "api-basic",
      "Duration": 3600,
      "MicroUnitPrice": 50000,
      "RateLimiters": [
        {
          "$type": "CallCount",
          "Count": 100
        }
      ]
    },
    {
      "RouteId": "api-premium",
      "Duration": 3600,
      "MicroUnitPrice": 200000,
      "RateLimiters": [
        {
          "$type": "CallCount",
          "Count": 1000
        },
        {
          "$type": "DataLimitDownload",
          "Bytes": 10485760
        }
      ]
    }
  ]
}

Content Streaming

"ApiChargeProxy": {
  "Routes": {
    "video-stream": {
      "ClusterId": "media-server",
      "Match": {
        "Path": "/videos/{**catch-all}"
      }
    }
  },
  "Clusters": {
    "media-server": {
      "Destinations": {
        "media-cdn": {
          "Address": "https://media-cdn.example.com/"
        }
      }
    }
  }
}

"ApiChargeQuotes": {
  "Quotes": [
    {
      "RouteId": "video-stream",
      "Duration": 7200,
      "MicroUnitPrice": 500000,
      "RateLimiters": [
        {
          "$type": "StreamRateDownload",
          "BytesPerSecond": 1048576,
          "WindowDurationInSeconds": 60
        },
        {
          "$type": "DataLimitDownload",
          "Bytes": 2147483648
        }
      ]
    }
  ]
}

AI Model API

"ApiChargeProxy": {
  "Routes": {
    "ai-inference": {
      "ClusterId": "ai-models",
      "Match": {
        "Path": "/ai/inference/{**catch-all}"
      }
    }
  },
  "Clusters": {
    "ai-models": {
      "Destinations": {
        "inference-server": {
          "Address": "http://localhost:8501/"
        }
      }
    }
  }
}

"ApiChargeQuotes": {
  "Quotes": [
    {
      "RouteId": "ai-inference",
      "Duration": 3600,
      "MicroUnitPrice": 1000000,
      "RateLimiters": [
        {
          "$type": "Credit",
          "CreditsPerCall": 1,
          "TotalCredits": 100
        }
      ]
    }
  ]
}

Next Steps

Now that you understand the basics of route configuration, you can explore: