API Monetization
This guide shows how to monetize your API endpoints using L402 payments.
Overview
L402 allows you to charge for API access at the endpoint level:
- Per-endpoint pricing - Different prices for different resources
- Path-based configuration - Protect paths with wildcards
- Zero configuration for clients - Standard HTTP 402 flow
- Works with any API - REST, GraphQL, WebSocket
Basic Setup
1. Enable L402
Add L402 configuration to your appsettings.json:
{
"L402": {
"Enabled": true,
"DefaultPriceSats": 100,
"ProtectedPaths": [
"/api/premium/*"
]
}
}
2. Register Middleware
In your ASP.NET Core application:
var builder = WebApplication.CreateBuilder(args);
// Add Lightning Enable services
builder.Services.AddLightningEnable(builder.Configuration);
builder.Services.AddL402Authentication();
var app = builder.Build();
// Add L402 middleware (before your API endpoints)
app.UseL402Authentication();
app.MapGet("/api/premium/data", () => new { message = "Premium content!" });
3. Test
# Without payment - returns 402
curl http://localhost:5096/api/premium/data
# With L402 credential - returns 200
curl http://localhost:5096/api/premium/data \
-H "Authorization: L402 <macaroon>:<preimage>"
Endpoint Pricing
Configure Different Prices
{
"L402": {
"DefaultPriceSats": 10,
"EndpointPricing": [
{
"PathPattern": "/api/ai/gpt4",
"PriceSats": 500,
"Description": "GPT-4 API call"
},
{
"PathPattern": "/api/ai/dalle",
"PriceSats": 1000,
"Description": "DALL-E image generation"
},
{
"PathPattern": "/api/data/*",
"PriceSats": 25,
"Description": "Data API access"
}
]
}
}
Path Matching
| Pattern | Matches |
|---|---|
/api/data | Exact path only |
/api/data/* | Any path starting with /api/data/ |
/api/*/status | /api/users/status, /api/orders/status |
Protecting Existing APIs
Attribute-Based Protection
[ApiController]
[Route("api/[controller]")]
public class PremiumController : ControllerBase
{
[HttpGet("data")]
[RequiresL402(PriceSats = 100)]
public IActionResult GetData()
{
return Ok(new { data = "Premium content" });
}
[HttpPost("analyze")]
[RequiresL402(PriceSats = 500)]
public IActionResult Analyze([FromBody] AnalysisRequest request)
{
return Ok(new { result = "Analysis complete" });
}
}
Programmatic Protection
app.MapGet("/api/premium/data", async (HttpContext context, IL402Service l402) =>
{
// Check for valid L402 credential
if (!await l402.ValidateRequestAsync(context, priceSats: 100))
{
// Returns 402 with invoice
return Results.StatusCode(402);
}
return Results.Ok(new { data = "Premium content" });
});
Client Integration
JavaScript Client
class L402Client {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.credential = null;
}
async request(endpoint) {
const headers = {};
if (this.credential) {
headers['Authorization'] =
`L402 ${this.credential.macaroon}:${this.credential.preimage}`;
}
const response = await fetch(`${this.baseUrl}${endpoint}`, { headers });
if (response.status === 402) {
const challenge = await response.json();
return {
needsPayment: true,
invoice: challenge.l402.invoice,
amount: challenge.l402.amount_sats,
macaroon: challenge.l402.macaroon
};
}
return response.json();
}
setCredential(macaroon, preimage) {
this.credential = { macaroon, preimage };
}
}
// Usage
const client = new L402Client('https://api.example.com');
const result = await client.request('/api/premium/data');
if (result.needsPayment) {
// Display invoice to user
console.log(`Please pay ${result.amount} sats`);
console.log(`Invoice: ${result.invoice}`);
// After user pays and provides preimage
client.setCredential(result.macaroon, preimage);
// Retry
const data = await client.request('/api/premium/data');
console.log(data);
}
Python Client
import requests
class L402Client:
def __init__(self, base_url):
self.base_url = base_url
self.credential = None
def request(self, endpoint):
headers = {}
if self.credential:
headers['Authorization'] = f"L402 {self.credential['macaroon']}:{self.credential['preimage']}"
response = requests.get(f"{self.base_url}{endpoint}", headers=headers)
if response.status_code == 402:
data = response.json()
return {
'needs_payment': True,
'invoice': data['l402']['invoice'],
'amount': data['l402']['amount_sats'],
'macaroon': data['l402']['macaroon']
}
return response.json()
def set_credential(self, macaroon, preimage):
self.credential = {'macaroon': macaroon, 'preimage': preimage}
cURL Example
#!/bin/bash
# Step 1: Request protected endpoint
RESPONSE=$(curl -s -w "\n%{http_code}" \
https://api.example.com/api/premium/data)
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" == "402" ]; then
INVOICE=$(echo "$BODY" | jq -r '.l402.invoice')
MACAROON=$(echo "$BODY" | jq -r '.l402.macaroon')
echo "Pay this invoice: $INVOICE"
echo "Enter preimage after payment:"
read PREIMAGE
# Step 2: Retry with credential
curl https://api.example.com/api/premium/data \
-H "Authorization: L402 $MACAROON:$PREIMAGE"
fi
Advanced Configuration
Token Validity
Control how long tokens remain valid:
{
"L402": {
"DefaultTokenValiditySeconds": 3600,
"EndpointPricing": [
{
"PathPattern": "/api/one-time/*",
"PriceSats": 1000,
"TokenValiditySeconds": 60
}
]
}
}
Service Tiers
Create different service tiers:
{
"L402": {
"ServiceTiers": [
{
"Name": "basic",
"Paths": ["/api/basic/*"],
"PriceSats": 10,
"RateLimit": 100
},
{
"Name": "premium",
"Paths": ["/api/premium/*"],
"PriceSats": 100,
"RateLimit": 1000
},
{
"Name": "enterprise",
"Paths": ["/api/enterprise/*"],
"PriceSats": 500,
"RateLimit": 10000
}
]
}
}
Custom Caveats
Add custom restrictions to macaroons:
services.AddL402Authentication(options =>
{
options.AddCaveat = (context, caveats) =>
{
// Add IP restriction
caveats.Add($"ip = {context.Connection.RemoteIpAddress}");
// Add request limit
caveats.Add("requests = 100");
// Add custom data
caveats.Add($"user_tier = {GetUserTier(context)}");
};
});
Analytics
Track L402 Usage
services.AddL402Authentication(options =>
{
options.OnPaymentVerified = async (context, payment) =>
{
await _analytics.TrackAsync(new L402Event
{
PaymentHash = payment.PaymentHash,
Endpoint = context.Request.Path,
AmountSats = payment.AmountSats,
Timestamp = DateTime.UtcNow
});
};
});
View Analytics
Query payment analytics:
curl https://api.lightningenable.com/api/l402/analytics \
-H "X-API-Key: your-api-key"
{
"period": "last_30_days",
"totalPayments": 1523,
"totalSatsEarned": 152300,
"topEndpoints": [
{ "path": "/api/ai/gpt4", "payments": 892, "sats": 89200 },
{ "path": "/api/data/market", "payments": 631, "sats": 63100 }
]
}
Best Practices
Pricing Strategy
- Start low - Lower barrier to entry
- Value-based pricing - Charge more for expensive operations
- Free tier - Consider some free endpoints for discovery
- Consistent pricing - Similar endpoints should cost similar amounts
Error Handling
app.MapGet("/api/premium/data", async (HttpContext context, IL402Service l402) =>
{
try
{
var result = await l402.ValidateRequestAsync(context, priceSats: 100);
if (!result.IsValid)
{
// Log for analytics
_logger.LogInformation("L402 challenge issued for {Path}", context.Request.Path);
return result.ChallengeResponse;
}
return Results.Ok(new { data = "Premium content" });
}
catch (L402Exception ex)
{
_logger.LogError(ex, "L402 validation error");
return Results.Problem("Payment validation failed");
}
});
Testing
# Test with curl
curl -i https://api.example.com/api/premium/data
# Verify 402 response includes all required fields
# - macaroon
# - invoice
# - amount_sats
# - payment_hash
Next Steps
- Proxy Configuration - Monetize any API
- API Reference - Complete L402 API documentation
- FAQ - Common questions