Maintain a blacklist that will be auto-populated when too many rejected requests are made from the same IP.
Create a blacklist store.
Record rejected attempts from each IP separately.
Start denying access to an IP regardless of other conditions once certain thresholds are met.
Consider different weights for rejected requests without a key vs invalid key vs valid key but already in use.
Consider implications for multi-service support (if there are multiple whitelists should there be multiple blacklists?)
Consider using tiny-lru so that rejected entries will auto-expire, it would make it much easier to count entries per time unit (e.g. lru.size divided by the time difference between lru.first and lru.last) – and tiny-lru is a (secondary) dependency already anyway.
Maintain a blacklist that will be auto-populated when too many rejected requests are made from the same IP.