Skip to main content

Proxy Configuration

The L402 Proxy allows you to monetize any API - yours or third-party - by creating a payment-gated reverse proxy.

No Infrastructure Required

The L402 proxy is a fully hosted service. You configure it; Lightning Enable runs it. No Lightning node, no servers, no Docker.

Overview

The proxy sits between clients and target APIs:

Client → L402 Proxy → Target API

└─ Requires Lightning payment

Use cases:

  • Monetize your own APIs — in Proxy mode (no modification) or via native middleware (one-line drop-in)
  • Resell third-party APIs with a markup
  • Create premium access to public APIs
  • Rate-limit expensive APIs via micropayments

Creating a Proxy

Navigate to api.lightningenable.com/dashboard/proxies/create and follow the 4-step wizard:

  1. Basic Info — name and description
  2. Target URL — the API you want to monetize
  3. Pricing — satoshis per request + token validity
  4. Review — confirm and create

Create Proxy Wizard

For a full visual walkthrough, see the Dashboard Guide.

Via API

curl -X POST https://api.lightningenable.com/api/proxy \
-H "X-API-Key: your-merchant-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Weather API",
"targetBaseUrl": "https://api.weather.com/v1",
"defaultPriceSats": 10,
"description": "Weather data with Lightning payments"
}'

Response (201 Created):

{
"id": 12,
"proxyId": "premium-weather-api-a1b2",
"name": "Premium Weather API",
"description": "Weather data with Lightning payments",
"targetBaseUrl": "https://api.weather.com/v1",
"defaultPriceSats": 10,
"isActive": true,
"createdAt": "2026-07-03T12:00:00Z",
"requestCount": 0,
"totalSatsEarned": 0,
"endpointPricingCount": 0,
"proxyUrl": "/l402/proxy/premium-weather-api-a1b2",
"endpointPricings": []
}

The proxyId slug is generated from the proxy name. If the slug collides with an existing proxy, a 4-character hex suffix is appended (as in the example above); otherwise the clean slug is used as-is.

Proxy Endpoint

Your proxy is now available at:

https://api.lightningenable.com/l402/proxy/{proxyId}/{path}

For example:

https://api.lightningenable.com/l402/proxy/premium-weather-api-a1b2/forecast?city=nyc

Configuration Options

Configuration Reference

Create (POST /api/proxy) accepts:

FieldTypeRequiredDescription
namestringYesDisplay name for the proxy (1–100 chars). Also used to generate the proxyId slug.
targetBaseUrlstringYesBase URL of the target API. Must be http/https with a public domain name (see SSRF Protection).
defaultPriceSatsintNoDefault price per request in satoshis (1–1,000,000). Defaults to 10 if omitted.
descriptionstringNoProxy description (up to 500 chars).

Update (PUT /api/proxy/{proxyId}) accepts the same fields — all optional — plus:

FieldTypeRequiredDescription
isActiveboolNoEnable/disable the proxy. Disabled proxies return 404 to clients.

There are no per-proxy method filters, path allow/block lists, or timeout settings. The proxy forwards all HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) and all paths under /l402/proxy/{proxyId}/* to the target API, with a fixed 30-second upstream timeout. If you need to restrict which endpoints are reachable, enforce that on your upstream API itself, or point targetBaseUrl at a narrower base path (e.g., https://api.example.com/v1/public).

No path filtering

Because there is no allowedPaths/blockedPaths setting, any path on your target API under the configured base URL is reachable through the proxy (after payment). Never point a proxy at an API surface that includes admin or internal endpoints you don't want exposed.

Token Validity

Each proxy has a configurable token validity period — how long a paid L402 token remains usable after payment. Set it in the dashboard wizard (Step 3: Pricing) when creating the proxy; if not set, tokens fall back to the global default of 1 hour.

note

Per-proxy token validity was fixed in the July 2026 update; earlier tokens always used the 1-hour default regardless of the value entered in the wizard.

Endpoint-Specific Pricing

Dashboard Alternative

You can manage endpoint-specific pricing visually in the dashboard — no API calls needed.

Set different prices for different endpoints:

curl -X POST https://api.lightningenable.com/api/proxy/{proxyId}/pricing \
-H "X-API-Key: your-merchant-api-key" \
-H "Content-Type: application/json" \
-d '{
"pathPattern": "/forecast/7day",
"priceSats": 50,
"description": "7-day forecast (premium)"
}'

Multiple Price Tiers

# Add pricing for different endpoints
curl -X POST .../pricing -d '{"pathPattern": "/current/*", "priceSats": 5}'
curl -X POST .../pricing -d '{"pathPattern": "/forecast/1day", "priceSats": 10}'
curl -X POST .../pricing -d '{"pathPattern": "/forecast/7day", "priceSats": 50}'
curl -X POST .../pricing -d '{"pathPattern": "/historical/*", "priceSats": 100}'

Price Matching Priority

Pricing rules are evaluated in ascending priority order (lower number = checked first; the default is 0). The first active rule whose pathPattern matches the request path wins — there is no specificity ordering beyond that. If no rule matches, the proxy's defaultPriceSats applies.

If you have overlapping patterns (e.g., /forecast/7day and /forecast/*), give the more specific rule a lower priority value so it is checked first:

curl -X POST .../pricing -d '{"pathPattern": "/forecast/7day", "priceSats": 50, "priority": 0}'
curl -X POST .../pricing -d '{"pathPattern": "/forecast/*", "priceSats": 10, "priority": 10}'

Using the Proxy

Request Flow

  1. Client requests proxy endpoint (no auth):
curl https://api.lightningenable.com/l402/proxy/premium-weather-api-a1b2/forecast?city=nyc
  1. Proxy returns 402 with invoice:
{
"error": "Payment Required",
"message": "Pay the Lightning invoice to access this API",
"proxy": {
"id": "premium-weather-api-a1b2",
"name": "Premium Weather API",
"description": "Weather data with Lightning payments"
},
"l402": {
"macaroon": "AgEL...",
"invoice": "lnbc100n1p...",
"amount_sats": 10,
"payment_hash": "abc123...",
"expires_at": "2026-07-03T12:10:00.0000000Z"
},
"instructions": {
"step1": "Pay the Lightning invoice using any Lightning wallet",
"step2": "Copy the preimage (proof of payment) from your wallet",
"step3": "Include in request: Authorization: L402 <macaroon>:<preimage> (or Authorization: Payment method=\"lightning\", preimage=\"<preimage>\")"
}
}
  1. Client pays invoice, gets preimage

  2. Client retries with L402 credential:

curl https://api.lightningenable.com/l402/proxy/premium-weather-api-a1b2/forecast?city=nyc \
-H "Authorization: L402 AgEL...:abc123..."
  1. Proxy forwards request to target, returns response:
{
"city": "New York",
"forecast": [...]
}

Proxy Management

List Your Proxies

curl https://api.lightningenable.com/api/proxy \
-H "X-API-Key: your-merchant-api-key"

Get Proxy Details

curl https://api.lightningenable.com/api/proxy/{proxyId} \
-H "X-API-Key: your-merchant-api-key"

Update Proxy

curl -X PUT https://api.lightningenable.com/api/proxy/{proxyId} \
-H "X-API-Key: your-merchant-api-key" \
-d '{"defaultPriceSats": 20}'

Delete Proxy

curl -X DELETE https://api.lightningenable.com/api/proxy/{proxyId} \
-H "X-API-Key: your-merchant-api-key"

Target API Authentication

Lightning Enable does not store or inject upstream API credentials. The proxy forwards client headers transparently and strips the incoming Authorization header (it consumes that one for the L402 payment check). There is no authHeader/authValue configuration.

If the API you are proxying requires its own authentication (an API key, bearer token, signed requests), you have two options:

  1. Merchant-side auth shim — put a thin endpoint you control between the proxy and the credentialed API: the proxy targets your endpoint, and your endpoint attaches the credential when calling the upstream. See Handling APIs that require authentication in the setup walkthrough for the full pattern.
  2. Native integration — skip the proxy and add L402 payment gating directly inside your own API with the native middleware. Your code keeps full control of upstream credentials; they never leave your infrastructure.

Analytics

Dashboard Alternative

The same lifetime totals are visible on the proxy detail page's Overview tab in the dashboard. Per-day charts and per-path breakdowns are on the roadmap but not available yet.

Get Proxy Analytics

curl https://api.lightningenable.com/api/proxy/{proxyId}/analytics \
-H "X-API-Key: your-merchant-api-key"

Response — lifetime totals for the proxy (there is no time-period filtering or per-path breakdown):

{
"proxyId": "premium-weather-api-a1b2",
"name": "Premium Weather API",
"totalRequests": 5234,
"totalSatsEarned": 52340,
"averageRevenuePerRequest": 10.00,
"createdAt": "2026-05-01T09:00:00Z",
"lastUpdated": "2026-07-03T11:58:21Z"
}

Testing Proxy

Test Target Reachability

curl -X POST https://api.lightningenable.com/api/proxy/{proxyId}/test \
-H "X-API-Key: your-merchant-api-key"

Response:

{
"success": true,
"targetUrl": "https://api.weather.com/v1",
"statusCode": 200,
"statusDescription": "OK",
"responseTimeMs": 234,
"message": "Target API is reachable"
}

On failure, success is false and message explains why (e.g., "Request timed out after 10 seconds" or "Connection failed: ..."). The test request uses a 10-second timeout and runs the same SSRF checks as live proxy traffic.

Test with Specific Path

Pass the path as a query parameter (there is no JSON request body):

curl -X POST "https://api.lightningenable.com/api/proxy/{proxyId}/test?path=/forecast" \
-H "X-API-Key: your-merchant-api-key"

Example Configurations

Public Weather API Proxy

An API that needs no upstream credentials — the simplest case:

{
"name": "Weather Data",
"description": "Public weather data with Lightning payments",
"targetBaseUrl": "https://api.open-meteo.com/v1",
"defaultPriceSats": 5
}

Your Own API with Tiered Pricing

Point the proxy at your API and layer endpoint-specific prices on top of the default:

{
"name": "Market Data",
"description": "Market data API, pay-per-request",
"targetBaseUrl": "https://api.your-domain.com/v2",
"defaultPriceSats": 10
}

Endpoint pricing (added via POST /api/proxy/{proxyId}/pricing):

[
{ "pathPattern": "/quotes/realtime/*", "priceSats": 50, "priority": 0 },
{ "pathPattern": "/quotes/*", "priceSats": 10, "priority": 10 }
]

Credentialed Upstream (via Auth Shim)

For an upstream that requires its own API key (e.g., an AI model provider), target a thin endpoint you host that attaches the credential — the proxy itself never holds it:

{
"name": "AI Completions",
"description": "Pay-per-request AI completions",
"targetBaseUrl": "https://shim.your-domain.com/ai",
"defaultPriceSats": 500
}

See Target API Authentication above for why this pattern is required and what the shim looks like.

Security

The L402 proxy includes several built-in protections to prevent abuse and ensure safe operation. These protections apply automatically to all proxies.

Request & Response Size Limits

The proxy enforces size limits on both inbound requests and upstream responses to prevent memory exhaustion and abuse.

LimitDefaultConfig KeyHTTP Status on Violation
Request body1 MB (1,048,576 bytes)L402:MaxProxyRequestBodyBytes413 Payload Too Large
Response body10 MB (10,485,760 bytes)L402:MaxProxyResponseBodyBytes502 Bad Gateway

Request body limit -- When a client sends a POST, PUT, or PATCH request through the proxy, the body is checked against the configured maximum. If the Content-Length header is present and exceeds the limit, the request is rejected immediately. If the header is absent, the body is read incrementally and rejected as soon as it exceeds the limit.

Response body limit -- When the upstream target API returns a response, its size is checked the same way: first via Content-Length header for an early rejection, then by streaming and monitoring the total bytes read. If the response exceeds the limit, the proxy returns a 502 to the client instead of the oversized response.

These limits are set at the Lightning Enable service level (L402:MaxProxyRequestBodyBytes / L402:MaxProxyResponseBodyBytes) — they are not per-proxy merchant settings. If your use case needs larger payloads, contact support.

Example error response (413):

{
"error": "Payload Too Large",
"message": "Request body size (2,500,000 bytes) exceeds the maximum allowed size (1,048,576 bytes)",
"proxy_id": "premium-weather-api-a1b2"
}

Example error response (502 for oversized upstream response):

{
"error": "Bad Gateway",
"message": "Response from target API (15,000,000 bytes) exceeds the maximum allowed size (10,485,760 bytes)",
"proxy_id": "premium-weather-api-a1b2"
}

SSRF Protection

Server-Side Request Forgery (SSRF) protections prevent proxies from being used to access internal infrastructure. Validation happens at two stages: when configuring a proxy and at runtime when forwarding each request.

Configuration-Time Validation

When you create or update a proxy, the targetBaseUrl is validated against these rules:

RuleDetails
SchemeOnly http and https are allowed. Other schemes (e.g., file://, ftp://) are rejected.
HostnameRaw IP addresses are not allowed; a domain name is required.
Localhost rejectionlocalhost, localhost.localdomain, ip6-localhost, and ip6-loopback are blocked.
Internal domain suffixesHostnames ending in .local, .internal, .localhost, or .svc.cluster.local are blocked.
Port restrictionsOnly standard HTTP ports (80, 443) are allowed. Non-standard ports are rejected.
DNS resolution checkIf the hostname resolves at configuration time, all resolved IPs are checked for private ranges. If DNS resolution fails (e.g., the domain is not yet set up), the proxy is allowed but will be checked again at runtime.

Runtime DNS Rebinding Prevention

Even if a domain passes configuration-time validation, it is checked again on every proxied request. Before connecting to the target API, the proxy resolves the hostname and verifies that none of the resolved IP addresses fall into private or reserved ranges. This prevents DNS rebinding attacks where an attacker changes a domain's DNS records to point to internal IPs after the proxy is configured.

Blocked IP ranges:

RangeDescription
127.0.0.0/8IPv4 loopback
10.0.0.0/8RFC 1918 private
172.16.0.0/12RFC 1918 private
192.168.0.0/16RFC 1918 private
169.254.0.0/16IPv4 link-local
0.0.0.0/8Current network
::1IPv6 loopback
fc00::/7IPv6 unique local
fe80::/10IPv6 link-local
IPv4-mapped IPv6e.g., ::ffff:127.0.0.1 (mapped to IPv4 and checked)

If a blocked IP is detected at runtime, the proxy returns a 502 Bad Gateway response:

{
"error": "Bad Gateway",
"message": "The target API address is not allowed",
"proxy_id": "premium-weather-api-a1b2"
}
tip

These protections are fully automatic. You do not need to configure anything -- they are always active for all proxies.

Error Handling

Proxy Errors

StatusBody errorCauseSolution
404Proxy not foundUnknown or disabled proxy IDCheck the proxy ID; re-enable the proxy if you disabled it
404Proxy unavailableThe merchant account behind the proxy is inactiveReactivate the merchant account
404Ambiguous proxy IDAn unsuffixed alias matches more than one proxyUse the full proxy ID including its hex suffix
502Bad GatewayTarget API unreachable / connection failedCheck the target URL and target API health
504Gateway TimeoutTarget API didn't respond within 30 secondsSpeed up the upstream endpoint (the timeout is fixed)
413Payload Too LargeRequest body exceeds size limitReduce request body or contact provider about limits
502Bad Gateway (size)Upstream response exceeds size limitContact provider about response size limits
502Bad Gateway (SSRF)Target resolves to private IPUse a public domain name for your target API

Error bodies include a message field with details and (for gateway errors) a proxy_id field.

Payment / Credential Errors

The proxy never returns 401 or 403. Any missing, malformed, expired, or otherwise invalid L402 credential results in a fresh 402 Payment Required challenge — a new invoice and macaroon — with the failure reason in the X-L402-Error response header and in the body's message field.

StatusMeaningWhat the client should do
402 (no X-L402-Error)First request — payment requiredPay the invoice, retry with Authorization: L402 <macaroon>:<preimage>
402 + X-L402-ErrorThe presented credential failed verification (bad format, wrong preimage, expired token, wrong path/merchant/price)Read the header for the reason; pay the new invoice from this response and retry

Clients that branch on 401/403 will never hit those branches — treat every 402 as a (re-)challenge.

Best Practices

Security

  • Keep upstream API credentials on your own infrastructure (auth shim or native integration) — the proxy does not store them
  • Scope targetBaseUrl to the narrowest base path that serves your paying clients; every path under it is reachable
  • Never expose an API surface containing admin or internal endpoints through a proxy
  • Monitor usage for abuse
  • Use HTTPS target URLs whenever possible

Pricing

  • Research target API costs
  • Add reasonable markup (20-50%)
  • Consider volume discounts
  • Price based on value, not cost

Reliability

  • Keep upstream responses under the fixed 30-second proxy timeout
  • Handle target API errors gracefully
  • Monitor target API availability
  • Have fallback targets if possible

Next Steps