MMeerPartners docs

API: rate limits and idempotency

MeerPartners API rate limiting and idempotency: postback and sign-in limits, the Retry-After header, Idempotency-Key and postback deduplication by external_order_id.

To make the integration resilient, the API limits the request rate and guarantees safe retries. This page is about both mechanisms: what happens when a limit is exceeded and how to retry requests without creating duplicates.

Rate limiting

Limits are counted in a fixed window (a counter in Redis). The limit key depends on the endpoint:

EndpointLimitKeyCode on exceeding
POST /api/v1/postback~600 / minper tenant_api_key (key_id)POSTBACK_RATE_LIMITED (429)
POST /api/v1/auth/meerid/exchange20 / minper IPRATE_LIMIT_EXCEEDED (429)
GET /r/{code}, GET /p/{code}.gifanti-floodper IP hashTRACKING_RATE_LIMITED (429)

Exact values are up to the operator

The numbers above are the defaults (postback — 600 requests per minute per key, sign-in — 20 per minute per IP). The platform operator can change them. Plan for exponential backoff, not fixed pauses.

The Retry-After header

On a 429 response, the platform tells you when you can retry via the Retry-After header (seconds), and the body contains the limit and the window:

429 response
{
  "error": {
    "code": "POSTBACK_RATE_LIMITED",
    "message": "Too many postbacks",
    "details": { "limit": 600, "window_seconds": 60 }
  }
}
HTTP/1.1 429 Too Many Requests
Retry-After: 23

How to handle 429

Don't hammer the endpoint in a loop. On a 429, wait Retry-After seconds (or use exponential backoff with jitter) and retry. For the postback, a retry is safe — it is idempotent by external_order_id (see below).

Postback is protected regardless of limits

Even under a load spike, postback intake is protected by the HMAC signature and the timestamp check, and idempotency by external_order_id prevents double-counting on retries.

Idempotency

Idempotency-Key

On mutating POSTs that create a resource or move money (/affiliate/links, /payouts, /merchant/offers, key issuance and payment operations), pass the header:

Idempotency-Key: 3f8a1c2e-7b3d-4c1a-9f2e-1a2b3c4d5e6f
Idempotency-Keystringoptional
A UUID v4 generated by the client. A repeat of the same request with the same key and the same body returns the saved response.

Behavior on a repeat:

  • Same key + same body → the saved response is returned (the same 2xx) with the header Idempotent-Replay: true. The side effect (creation, debit) is executed exactly once.
  • Use the same key on retries due to a network timeout — that way you won't create a second offer/a second withdrawal request.

Postback deduplication

The postback has its own, stronger idempotency — by external_order_id, so Idempotency-Key is secondary for it and not required.

  • The deduplication key is the business's external_order_id (combined with the tenant and the event type).
  • A repeated postback with the same external_order_id does not create a second conversion: the response is 200 with "duplicate": true and the same conversion_id.
  • Concurrent simultaneous postbacks are serialized by an advisory lock — a race does not lead to a duplicate.
  • The dedup record is stored permanently; for practical retries, rely on a 24-hour window — within it, a repeat is guaranteed to return the original result.
Repeated postback → 200
{ "accepted": true, "duplicate": true, "conversion_id": 778 }

Uniqueness of external_order_id

external_order_id must be unique on the business side for each order. If you reuse it for different conversions, the second event will be treated as a duplicate and will not be counted.

Signature anti-replay (S2S)

For signed S2S requests (postback, token-exchange), there is additional protection against replaying an intercepted request:

  • X-Timestamp must be within ±300 seconds of the server time, otherwise POSTBACK_STALE_TIMESTAMP (400).
  • The (key_id, signature) pair is marked as used in Redis for 600 seconds; a repeat of the same signature → POSTBACK_REPLAY (409).

This means: sign each S2S request anew with a current X-Timestamp, and do retries precisely by re-sending the same external_order_id (logical idempotency), not by resending the byte-for-byte same signature. Signature details are on the Postback page.

What's next