Rate limits
WinkPG rate limits the API. This page describes what to build against: the 429 response, the headers that report your allowance, and how to retry.
Why there's no published limit
Limits are tuned per deployment. They can also be retuned for a single merchant or API key at any time. Any number here would already be wrong for some deployment, so this page names no ceiling. A client that paces itself against a stale figure either throttles itself needlessly or exceeds a limit it didn't know about.
- Handle the 429 response. It's the contract, and it's the same on every deployment.
- Read your allowance from the response headers instead of counting requests against an assumed ceiling.
- Treat a 429 as normal feedback, not a fault. A queue-driven integration that honors Retry-After takes one in stride.
The 429 response
A rejected request returns HTTP 429, a Retry-After header in whole seconds, and an application/problem+json body.
{
"type": "https://httpstatuses.io/429",
"title": "Too Many Requests",
"status": 429,
"detail": "The request was rejected because a rate limit was exceeded.",
"retryAfterSeconds": 12
}Retry-After and retryAfterSeconds carry the same value, so read whichever suits your HTTP client. The value is always at least one second. The body doesn't name the limit, its scope, or an identifier. Don't make your client depend on knowing which internal limit you reached.
Response headers
You don't have to wait for a 429 to see your allowance. Both rate-limited and successful responses carry these headers when a limit applies.
| Header | Value |
|---|---|
X-RateLimit-Limit |
The ceiling for the limit that applied, as a whole number. When several limits apply, this reports the one that bound the request, which isn't always the largest. |
X-RateLimit-Remaining |
How much of that ceiling is left. Never below zero. |
Retry-After |
On a 429 only. How long to wait before you retry, in whole seconds. |
The two headers arrive together or not at all. Absent doesn't mean zero. It means WinkPG measured no allowance for that request. That happens when no limit covers the endpoint you called, and in one case where WinkPG reports nothing by design rather than a figure it can't stand behind. Read the pair when it's present, and continue normally when it isn't.
WinkPG doesn't send a reset header. Retry-After tells you how long to wait, and it arrives on the response that needs it.
What a limit covers
WinkPG declares limits per category of endpoint, not per operation. A group of endpoints shares one allowance.
- Spreading calls across endpoints in the same category doesn't increase your allowance. The allowance belongs to the category.
- WinkPG measures a limit against something specific to the caller, usually your API key. Another integrator's traffic doesn't consume your allowance.
- Several limits can apply to one request. The headers report the one that bound it, which is the only one worth pacing against.
- The API reference marks every operation that can return 429, so a generated client includes the branch before you ever reach a limit.
Retry after a 429
- Wait at least as long as Retry-After specifies. An earlier retry is rejected again, and the rejected attempt still counts against a request limit.
- Back off exponentially after the first retry, and add random jitter. Otherwise a fleet of workers that all hit the limit returns at the same moment.
- Cap the number of retries. Report a failure instead of looping.
- Send an idempotency key with any retry that creates a resource. An unnecessary retry then replays instead of acting twice.
Test your retry path
You don't need real traffic to test this. On a test key, the X-WinkPG-Mock-Error request header forces a 429 on demand. The forced response carries the same headers and body shape as a real one, so you can verify your retry path before it matters.
Sandbox transaction allowances
A trial sandbox can also be capped on how many sandbox transactions WinkPG processes for it. The cap is separate from the rate limits above, and it has its own 429.
- Only calls that create a sandbox transaction on a test key count against the allowance. Reads, and calls that change configuration, are never refused because an allowance is spent.
- A refused call returns HTTP 429 with the code USAGE_BUDGET_EXCEEDED. The body isn't the problem shape above. It carries error, code and message, plus limit (the allowance) and used (how much of it you've spent).
- A daily allowance resets at midnight UTC. Its refusal carries Retry-After, set to the time left until the reset, so the retry advice above applies.
- A total allowance doesn't reset, and its refusal carries no Retry-After. Retrying won't help. Talk to your integration contact if you need to keep testing.
- A trial also has an end date. After it, every call is refused with the code TRIAL_EXPIRED, whatever is left of the allowance. Your API keys stay valid, and they work again if your integration contact extends the trial.