Listing Your API on RapidAPI: Revenue Share, Proxy Auth and Quota Mapping
A marketplace listing is a distribution channel, not a business model. That distinction decides everything else on this page. Part of the Building API Marketplaces guide, this article resolves one specific question: should you put your commercial Python API on RapidAPI, and if you do, how do you wire the gateway into your own auth and quota system without handing over the customer relationship? The short version of the verdict — argued properly further down — is list it as a secondary channel, keep direct Stripe billing as the primary one, and never let the marketplace become the only place a customer can buy.
The revenue share maths
Marketplaces charge a percentage of collected revenue. RapidAPI's published commission has sat at 20% of what your subscribers pay, and the platform handles card processing, invoicing, tax handling and dunning out of that cut. Compare that against selling directly: card processing costs roughly 2.9% plus 30 cents per charge, so a $29 monthly plan clears about $27.86 into your account. The same plan sold through the marketplace clears $23.20. The gap is $4.66 per subscriber per month, or 16.7% of gross.
That number is small in isolation and enormous in aggregate. At 100 paying subscribers on a $29 plan you are handing over $466 a month — roughly the cost of a decent managed Postgres instance plus your entire compute bill. At 500 subscribers it is $2,330 a month, which is real salary money for a side hustle. The honest framing is this: the commission is a customer acquisition cost, and you should evaluate it exactly like paid ads. If the marketplace brings you subscribers you would never have found, 20% is cheap. If it merely intercepts people who would have signed up on your own site anyway, 20% is a tax on traffic you already owned.
Run the arithmetic on your own numbers before you commit. Keep the rates in environment variables so you can re-run the model when a platform changes its terms.
"""Channel margin model. Usage: python channel_margin.py"""
import os
from decimal import Decimal, ROUND_HALF_UP
def money(value: Decimal) -> Decimal:
return value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
def net_direct(gross: Decimal) -> Decimal:
pct = Decimal(os.getenv("CARD_FEE_PCT", "0.029"))
fixed = Decimal(os.getenv("CARD_FEE_FIXED", "0.30"))
return money(gross - (gross * pct) - fixed)
def net_marketplace(gross: Decimal) -> Decimal:
share = Decimal(os.getenv("MARKETPLACE_REV_SHARE", "0.20"))
return money(gross * (Decimal(1) - share))
if __name__ == "__main__":
price = Decimal(os.getenv("PLAN_PRICE_USD", "29.00"))
seats = int(os.getenv("SUBSCRIBER_COUNT", "100"))
split = Decimal(os.getenv("MARKETPLACE_SHARE_OF_SUBS", "0.30"))
direct_seats = Decimal(seats) * (Decimal(1) - split)
market_seats = Decimal(seats) * split
blended = money(direct_seats * net_direct(price) + market_seats * net_marketplace(price))
print(f"direct per sub {net_direct(price)}")
print(f"marketplace per sub {net_marketplace(price)}")
print(f"blended monthly {blended}")
print(f"commission drag {money(Decimal(seats) * net_direct(price) - blended)}")
Two costs never show up in that model and both matter. Payouts arrive on a delay measured in weeks rather than days, so a marketplace-heavy revenue mix worsens your cash conversion cycle exactly when you are paying infrastructure bills monthly. And you do not get the customer's email address — support arrives through the platform, churn arrives without warning, and you cannot run a price increase or a win-back campaign against a list you do not hold.
The proxy secret is your entire perimeter
When a consumer calls your API through the marketplace, the request hits the platform gateway first. The gateway authenticates the subscriber against its own key, meters the call against their plan, then forwards the request to your origin with a set of injected headers. The critical one is X-RapidAPI-Proxy-Secret: a per-listing shared secret proving the request genuinely came through the gateway and not from someone who found your origin hostname.
If you skip that check, your origin is an open API. Anyone who reads a Host header in a network trace can call you directly, bypass the meter entirely, and you will pay the compute while the marketplace shows zero usage. Verify the secret in a dependency, compare it with hmac.compare_digest so you are not leaking timing information, and reject with 403 before any business logic runs. This slots naturally into an async-native FastAPI setup.
Two hardening rules go with that check. First, lock your origin at the network edge as well: if your host supports it, allow only the gateway's egress ranges on the marketplace hostname and serve direct customers on a separate hostname. Defence in depth costs nothing here. Second, treat the proxy secret as a rotating credential, not a constant — accept a current and a previous value during a rotation window, exactly as you would when rotating API keys without downtime.
import hmac
import os
from dataclasses import dataclass
from typing import Annotated
from fastapi import Depends, Header, HTTPException, status
def _accepted_proxy_secrets() -> tuple[str, ...]:
raw = os.getenv("RAPIDAPI_PROXY_SECRETS", "")
return tuple(s.strip() for s in raw.split(",") if s.strip())
@dataclass(frozen=True, slots=True)
class Caller:
channel: str # "marketplace" or "direct"
account_id: str
plan: str
async def marketplace_caller(
proxy_secret: Annotated[str | None, Header(alias="X-RapidAPI-Proxy-Secret")] = None,
rapid_user: Annotated[str | None, Header(alias="X-RapidAPI-User")] = None,
subscription: Annotated[str | None, Header(alias="X-RapidAPI-Subscription")] = None,
) -> Caller | None:
if proxy_secret is None:
return None
accepted = _accepted_proxy_secrets()
if not any(hmac.compare_digest(proxy_secret, s) for s in accepted):
raise HTTPException(status.HTTP_403_FORBIDDEN, "Invalid gateway credential")
if not rapid_user:
raise HTTPException(status.HTTP_400_BAD_REQUEST, "Missing subscriber identity")
return Caller(
channel="marketplace",
account_id=f"rapidapi:{rapid_user}",
plan=(subscription or "BASIC").upper(),
)
Note the Header(alias=...) form. FastAPI will happily map x_rapidapi_proxy_secret to the header name by convention, but the explicit alias survives refactors and reads unambiguously to the next person. Note also that marketplace_caller returns None rather than raising when the header is absent — that is what lets one route serve both channels.
Mapping marketplace plans onto your own quotas
The gateway meters calls against the plan the subscriber bought on the platform. You still need your own counter. Platform metering is eventually consistent from your side, gives you no per-endpoint cost visibility, and disappears the moment you delist. The rule: the marketplace plan name is an input to your quota system, never a replacement for it.
Keep the mapping in a TOML file rather than a dict buried in a module. Pricing changes are commercial decisions made under time pressure, and a config file can be edited and reviewed without a code deploy. The same table should drive your direct tiers, which keeps pricing tier design honest across both channels.
| Header value | Internal tier | Monthly quota | Rate limit |
|---|---|---|---|
BASIC | free | 500 | 2 rps |
PRO | starter | 25,000 | 10 rps |
ULTRA | growth | 250,000 | 40 rps |
MEGA | scale | 2,000,000 | 120 rps |
The fallback matters more than the mapping. A subscription header you do not recognise means the platform shipped a plan you have not configured, and the safe response is the free tier, not unlimited access. Get that backwards once and a misconfigured listing becomes a free unlimited endpoint you are paying to serve.
import functools
import os
import tomllib
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class Quota:
tier: str
monthly: int
per_second: int
@functools.lru_cache(maxsize=1)
def _plan_table() -> dict[str, Quota]:
path = os.getenv("PLAN_CONFIG_PATH", "config/plans.toml")
with open(path, "rb") as fh:
data = tomllib.load(fh)
return {
name.upper(): Quota(
tier=cfg["tier"],
monthly=int(cfg["monthly_quota"]),
per_second=int(cfg["rate_limit_per_second"]),
)
for name, cfg in data["plans"].items()
}
def resolve_quota(caller: Caller) -> Quota:
table = _plan_table()
match caller:
case Caller(channel="marketplace", plan=plan) if plan in table:
return table[plan]
case Caller(channel="marketplace"):
return table["BASIC"] # unknown plan -> most restrictive
case Caller(channel="direct", plan=plan):
return table.get(plan.upper(), table["BASIC"])
raise ValueError("unroutable caller")
Enforce the resulting Quota in the same Redis counter both channels share, following the patterns in API rate limiting best practices. Record every call to your own store as well, because the platform's dashboard will not tell you which endpoint is eating your margin — logging usage events to Postgres will.
Verify both paths with one script before you publish the listing. If the direct call to your marketplace hostname succeeds without a proxy secret, you have a hole.
import asyncio
import os
import httpx
async def probe() -> None:
origin = os.getenv("ORIGIN_BASE_URL")
gateway = os.getenv("RAPIDAPI_GATEWAY_URL")
path = os.getenv("PROBE_PATH", "/v1/status")
async with httpx.AsyncClient(timeout=10.0) as client:
naked = await client.get(f"{origin}{path}")
proxied = await client.get(
f"{gateway}{path}",
headers={
"X-RapidAPI-Key": os.getenv("RAPIDAPI_TEST_KEY", ""),
"X-RapidAPI-Host": os.getenv("RAPIDAPI_HOST", ""),
},
)
print(f"unauthenticated origin -> {naked.status_code} (want 403)")
print(f"through gateway -> {proxied.status_code} (want 200)")
if __name__ == "__main__":
asyncio.run(probe())
When to list and when to skip
List when your API is a self-explanatory utility that a developer can evaluate in one request — geocoding, currency conversion, document parsing, an enrichment lookup. Marketplace buyers browse by capability and convert without a sales conversation, which suits exactly that shape of product. List when you are pre-launch with no audience and the alternative to a 20% commission is zero revenue. And list when your gross margin per call comfortably exceeds 30%, so the commission still leaves you a business; run the numbers with cost per API request before you decide.
Skip it when your unit economics are thin — anything that proxies an expensive upstream model or dataset, where a 20% haircut turns a 15% margin negative. Skip it when your product needs onboarding, contracts, or per-customer configuration, because marketplace buyers churn as impulsively as they subscribe. Skip it when you are already converting organic traffic on your own site at a healthy rate: at that point the listing mostly cannibalises full-price signups. And skip it if you cannot afford the cash flow delay, because payout lag punishes anyone running close to the line.
Migrating: adding the listing without moving the business
The switch is additive, not a replacement, and it takes an afternoon if you already have key-based auth working.
- Add a second hostname or route prefix for marketplace traffic. Keep the direct hostname untouched so existing customers see zero change.
- Put
marketplace_callerin front of the shared auth dependency and fall back to your existing bearer-key check when the proxy header is absent. One route, two credentials, oneCallerobject downstream. - Load the plan table from TOML and back-fill your existing direct tiers into the same file so both channels enforce identical numbers.
- Publish an accurate OpenAPI document — the marketplace imports it to generate the listing's endpoint list and code samples, so a sloppy schema costs you conversions. Tighten it first with OpenAPI documentation.
- Set the proxy secret as a two-value list in your deployment environment, run the probe script above, and confirm the naked origin call returns 403.
- Only then flip the listing public, and keep a link back to your own site in the listing description and in every response's documentation URL.
Going the other way — delisting — is easier because you never gave up the primary channel. Freeze new signups on the platform, email the subscribers you can reach through platform support with a discount code for direct signup, keep the gateway route alive for a full billing cycle, then return 410 with a migration link.
Builder verdict
List, but treat it as paid distribution with a fixed budget rather than a home for your business. The winner is the hybrid setup: direct Stripe billing as the primary channel with the marketplace running alongside as a discovery surface, targeting no more than about a third of revenue. That mix costs you roughly 5% of gross in blended commission — $140 a month on the 100-subscriber example versus $466 if you sold everything through the platform — while buying you a stream of developers you would otherwise pay ads to reach. The engineering cost is genuinely small: one dependency, one config file, one probe script, and the same quota counter you already run. The strategic cost is the part to watch. The day the marketplace is more than half your revenue, you are running someone else's channel, subject to their terms and their payout schedule, with no email list to fall back on. Build the developer portal on your own domain first, ship the listing second, and keep the ratio deliberate.
FAQ
Is the 20% commission worth it compared with running ads to my own site? Usually yes at small scale. A 20% commission on a $29 plan is $5.80 a month, so a subscriber who stays six months costs you about $35 in commission. Paid acquisition for developer tools rarely lands a converting signup for under $60. The commission only looks expensive once you have organic traffic converting on your own site, because then you are paying it on customers you already earned.
Does listing on a marketplace hurt my direct pricing? It does if you price the same plan lower on the platform to look competitive. Price identically across both channels, and put the extras — priority support, higher burst limits, an annual discount — only on your direct plans. That gives buyers a reason to come to you without making your marketplace listing look like a bait and switch.
What happens to my margin if a marketplace subscriber abuses the free tier? The platform meters the free plan, but it does not care about your compute bill, so enforce your own counter on marketplace calls exactly as you would on direct ones. Cap concurrency per account, not just monthly volume, and apply the same defences described in preventing free tier abuse.
How risky is rotating the proxy secret on a live listing?
Low risk if you accept a list of secrets rather than one. Add the new value to RAPIDAPI_PROXY_SECRETS, deploy, update the listing in the platform dashboard, wait for in-flight requests to drain, then drop the old value on the next deploy. Rotating with a single-value environment variable causes a 403 storm for however long the deploy takes.
Can I ship breaking changes to an API that is listed on a marketplace? Only with versioned paths. Marketplace subscribers never see your changelog and will not read a deprecation email you cannot send them, so a breaking change on a stable path produces silent failures and one-star reviews. Version the URL, list the new version as a separate endpoint group, and follow the guidance in versioning and evolving public APIs.
Related
- Building API Marketplaces — the parent guide covering marketplace strategy end to end.
- Python API Subscription Billing Tutorial — the direct-billing stack this page treats as your primary channel.
- Designing API Pricing Tiers — set the tier boundaries you map marketplace plans onto.
- Calculating Cost per API Request — work out whether a 20% commission still leaves a margin.
- Creating a Developer Portal for Your API — the owned channel that keeps the marketplace optional.