Skip to content

Caching & quotas

Amazon API quotas

Amazon throttles its product APIs per Associates account. Historically, new accounts start around 1 request per second and 8,640 requests per day, and the quota then grows with the revenue generated by your shipped items. Check the current rules in the Creators API documentation: when the quota is exceeded, Amazon rejects calls and AMW answers HTTP 503 with a Retry-After header until Amazon accepts calls again.

Caching layers

AMW is designed so that most page views never reach Amazon:

flowchart LR
    W["Widget<br/>in-memory cache<br/>(5 min, per page)"] --> B["Browser / CDN / proxy<br/>Cache-Control: max-age"]
    B --> A["AMW"]
    A --> R[("Redis<br/>Redis.expire")]
    R -. miss .-> Z["Amazon Creators API"]
Layer Controlled by Notes
Widget memory cache fixed, 5 minutes Avoids duplicate requests within a page session.
HTTP caches Server.httpCache Found products and widget.js carry Cache-Control: public, max-age=<httpCache>; errors, rate-limit answers and /health carry no-store, so a transient failure is never replayed by a cache.
NGINX micro-cache / CDN your proxy See Reverse proxy.
Redis Redis.enabled, Redis.expire, Redis.staleExpire Shared by all visitors; survives AMW restarts. Products are fresh for Redis.expire and kept Redis.staleExpire longer as a fallback.
Not-found cache Redis.notFoundExpire Remembers products Amazon does not know (in Redis, or in memory without Redis).

Amazon price freshness

The Amazon Associates Program requires the price and availability shown to visitors to be less than 24 hours old. AMW enforces it on the server side:

  • Redis.expire is capped at 86400 seconds (a larger value is lowered at startup, with a warning in the logs). The Docker default is 12 hours.
  • A cached product older than 24 hours is never served, not even as a fallback while Amazon is down: it is fetched again, or the Amazon error is returned.
  • The Cache-Control: max-age of a product response is min(Server.httpCache, 24 h - age of the product), so browsers and CDNs drop it before it turns 24 hours old.

Recommended production values: Redis.expire between a few hours and 24 hours, and an httpCache of a few hours (it also applies to widget.js).

Protecting the quota

  • Server.rateLimit caps /product calls per client IP and per minute (default 120).
  • Requests are validated before any cache or Amazon lookup, so malformed calls cost nothing.
  • Each lookup requests a single item and a minimal set of resources (image, title, brand, price).
  • Concurrent requests for the same uncached product share a single Amazon call.
  • Products Amazon does not know are remembered for Redis.notFoundExpire seconds.

When Amazon fails

  • If Amazon fails (error, timeout, throttling) and the cache still holds an expired copy of the product (kept Redis.staleExpire seconds after Redis.expire) that is less than 24 hours old, AMW serves that copy with HTTP 200; its timestamp tells how old it is.
  • After Amazon.breakerThreshold consecutive failures the circuit breaker opens: AMW stops calling Amazon for Amazon.breakerCooldown seconds, serves stale copies when it has them and otherwise answers HTTP 503 with a Retry-After header. A single trial call then decides whether Amazon is back.