Size a launch
This guide is for the developer or solution architect who is about to size a project: a storefront, some integrations and a few scripts, all calling one tenant (one merchant's store on the platform). By the end you have a request budget for that tenant, a list of the loops that must become batch calls, a compute size and timeout for each site, a backoff that keeps a 429 from becoming an outage, and a decision on warmup for each App.
The limits are documented across several pages, and they behave differently: some refuse a request, some quietly cut it down. The table below puts them side by side. The five sections after it are the design work, in the order you need it.
The short answer: treat 100 requests per second per tenant as the whole budget for every consumer together (as of 2026-09), replace per-item loops with batch calls, page lists to the last page, and give every client a limiter and a capped backoff.
The limits in one place
| Limit | Value (as of 2026-09) | What happens past it |
|---|---|---|
| API requests per tenant | By default 100 per second sustained, bursts up to 200. Shared by every key and user of the tenant | 429 with Retry-After. See Rate limits |
| Page size on a list | Up to 200 items per page and 50 by default on the lists checked. Limits are defined per endpoint, so check the Explorer for yours. See Pagination | A larger limit is clamped to 200, not refused |
| Items in a batch call | Up to 200 for prices/resolve and for the stock availability route | Refused with 400. Split the call into chunks of 200 or fewer |
Products in POST /v1/products/labels | Up to 500 ids and, separately, up to 500 skus | Anything past 500 in either list is ignored, not refused |
| Bulk data | Files through /v1/io, not entity routes. Price entries also have a bulk route | See Import and export and Price lists |
| Request time of a server-rendered site | A per-site setting, up to 30 seconds | The request fails when it runs out |
| How long the CLI waits for an App build | 600 seconds by default (--timeout). This is a setting of the CLI, not a platform limit | The CLI stops waiting. See Deploying |
| Site environment variable | Key up to 255 characters, value up to 8192 | The variable is over the limit and cannot be saved as it is |
What is not published (as of 2026-09): storage quotas, invocation quotas and pricing. The docs carry no numbers for them, so this guide does not either. If your design depends on one, ask through support.revenexx.com before you build on a guess.
1. Build a request budget per tenant
The 100 requests per second belong to the tenant, not to a key. Ten keys share the same bucket as one. Creating a separate key per job is good practice for revoking access, but it does not split the budget.
Everything that calls the tenant draws from it: the storefront's server, every integration, and every script someone runs by hand. Write the budget down before launch, one line per consumer. The numbers below are an illustration, not a recommendation:
| Consumer | How you count it | Requests per second |
|---|---|---|
| Storefront server | Pages rendered per second × API calls per page. 15 × 4 | 60 |
| Order sync to the ERP | One call per order, paced by the worker. About 10 orders per second at the daily peak | 10 |
| Catalog import | Deliberately slow: the job holds itself to 5 per second | 5 |
| Headroom for scripts and retries | Whatever is left after the rows above | 20 |
| Total | Stay below the sustained 100. The burst of 200 absorbs a spike; it is not extra capacity to plan on | 95 |
Two habits keep the budget honest:
- Count the storefront by its fan-out, not its traffic. A product page that makes four API calls costs four times what a visitor count suggests. Cut the fan-out first: one market context call replaces five reads, and one price call covers a whole listing (next section).
- Give bulk work its own pace. A backfill that runs flat out at 100 per second leaves the storefront to compete with it for the same bucket. Run it at a fixed share, or outside trading hours.
A 429 is a signal to slow down, not a setting you change in Cockpit. If a launch's budget does not fit in 100 per second even after batching and caching, tell support before the launch so the plan can be looked at with you, not after.
api.revenexx.com is answered by the origin and is not behind it.2. Swap loops for batch and bulk routes
Every loop that makes one call per item spends the budget one request at a time. Find them by asking, for each page and job: does it call the API inside a for or a map?
| The loop you have | What replaces it |
|---|---|
| A price per product on a listing or cart | One POST /v1/prices/resolve with up to 200 items, answers in request order |
| Stock per item | The stock availability route takes up to 200 items in one call. See Stock |
| A name for each product ID or SKU | One POST /v1/products/labels for up to 500 products |
| Reading a whole collection | Pages of 200, not 50. Ask for limit=200 and page on |
| Writing thousands of products or prices | A file through /v1/io, or for price entries the bulk route, not thousands of entity calls |
Three things catch people out:
- The caps are not all the same kind. A
limitabove 200 on a list is clamped:?limit=100000returns 200 rows and a200, not an error. So a loop that thinks it fetched everything in one call did not. Always page until the last page. labelsdrops the overflow without saying so. Send 800 SKUs and you get names for 500, with no error. The cap is 500 per list,idsandskusseparately. Chunk to 500 yourself.- An import still makes some calls. The file bytes go straight to object storage and never through the API, but minting the upload URL, registering the job and polling it are API calls. Poll every few seconds, not in a tight loop.
After a bulk catalog change, category rules and the completeness measurement do not update themselves. Recompute them as described on Import and export.
3. Pick compute and timeouts
Compute is a per-site setting. A site has one server specification that covers both the build and the running site, and there is no plan tier that unlocks a bigger one. Read the sizes from GET /v1/sites/specifications rather than from a table you copied. See Compute specifications.
Decide per site:
| Site | Size | Timeout |
|---|---|---|
| Server-rendered storefront | Not the smallest size. It does real work on every request | As short as your slowest page needs, never above the maximum |
| Static site | Barely matters. Nothing executes per request | Not relevant |
| A theme with a large block library | Raise the specification and Node's heap (--max-old-space-size) together. A bigger specification with the default heap fails the same way | Not relevant to the build |
Rules for sizing:
- Do not raise the timeout to fix a slow page. The request time is capped, and a slow page is almost always a slow upstream call on the render path. Render the shell on the server and fetch the buyer-specific parts, such as price and stock, after paint. See Build configuration.
- Read the request duration in the logs before you resize. See Logs.
- Give an App build room.
revenexx deploy appwaits 600 seconds by default. If a build is slow, raise--timeoutin your pipeline. The flag only changes how long the CLI waits. See Deploy with the CLI. - Keep environment variables small. A key is limited to 255 characters and a value to 8192. A certificate bundle or a large JSON document does not belong in one; store a reference and fetch the content at start.
4. Add 429 backoff, and stay under the limit in the first place
The SDKs do not retry (as of 2026-09). A 429 reaches your code as an error. The CLI does retry, but only methods that are safe to replay (GET, HEAD, PUT, DELETE, OPTIONS), never POST or PATCH. So every client you write needs two things.
A limiter, so you rarely see a 429 at all. Give each consumer its share from section 1 and enforce it in the client. This one limits how fast calls start. It does not cap how many run at once, so pair it with a concurrency limit if your worker fans out:
// At most `perSecond` calls start in any one second.
function makeLimiter(perSecond) {
const interval = 1000 / perSecond
let next = Date.now()
return async function limited(fn) {
const now = Date.now()
const wait = Math.max(0, next - now)
next = Math.max(now, next) + interval
if (wait) await new Promise(r => setTimeout(r, wait))
return fn()
}
}
const limited = makeLimiter(5) // the catalog import's share
// Inside an async function or an ES module:
await limited(() => callTheApi()) // your own function that makes one API call
A retry, for the 429 you still get. The response carries Retry-After, which is a fixed 1 (as of 2026-09). Honour it and add jitter so that many clients released by the same limit do not retry in the same instant. The backoff loop is on Rate limits; use it as it is.
Rules for both:
- A
429from the gateway means the request was not processed, because the gateway refuses it before any service sees it. So replaying it is safe, for any method. The CLI is more cautious and does not retryPOSTorPATCH. That is different from a timeout or a502on a write, which may have been applied. There is no idempotency header, so make writes repeatable with a natural key or check first. See Retries & idempotency. - Cap the retries and surface the error after the last one. An unbounded retry loop turns a spike into a sustained overload of your own making.
- Do not retry in a tight loop across workers. Ten workers that each back off on their own are ten workers hitting the limit again together. Share one limiter per process, and pace the process.
5. Decide on warmup per App
An App runs on demand, so the first request after a quiet period pays a cold start. warmup keeps the App warm on the platform's schedule. It is off by default and it is set per App in the manifest:
"warmup": true
Decide by asking who waits:
| The App | warmup |
|---|---|
| A capability a storefront or checkout awaits: prices, stock, cart | On |
| An admin-only App that an operator opens a few times a day | Off |
| A scheduled or event-driven job that nobody waits for | Off |
It is not free, which is why it is off by default. Switch it on where a buyer notices the delay, and measure that it helped before you spread it across every App. If the slow part of a request is your own code, warmup will not fix it. See Operating an App.
Before you launch
- The request budget is written down, one line per consumer, and the total is under 100 per second with headroom.
- No page or job calls the API inside a loop for something a batch route answers.
- Every list is paged to the last page, and
labelscalls are chunked to 500. - Each site has a size you chose, and a slow page is fixed on the render path, not with a longer timeout.
- Every client of the API has a limiter and a capped backoff with jitter.
- Environment variables fit the 255 and 8192 character limits, and the pipeline's
--timeoutallows for a slow App build. warmupis on for the Apps a buyer waits for and off elsewhere, and you measured that it helped.
Where to go next
- Go live with a project — the rest of what to settle before launch.
- Import a BMEcat catalog and Hand an order to the ERP — the loops that most often need pacing, worked through.
- Connect your own frontend — pricing in batches of 200 from a storefront server.
- Operating an App — what to watch once the budget is in use.
- Rate limits — the limit and the backoff loop.
- Pagination — paging a list to its end.
- Compute specifications — the sizes and the build runtime.