Rate limiting that
can't double-spend.

Atomic token-bucket math at the edge. One API call. Binary allowed / denied. Works with REST, GraphQL, gRPC and anything else.

terminal
# 1. Hash the user ID — never send raw PII
KEY=$(echo -n "[email protected]" | sha256sum | awk '{print $1}')

# 2. Check the rate limit
curl -X POST https://api.waitstate.io/v1/deduct \
  -H "Authorization: Bearer $WAITSTATE_DEDUCT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key":"'$KEY'","bucket":"graphql-api","cost":10}'

# 200 OK  ->  allowed
# 429     ->  denied, retry_after: 5

Distributed rate limiting
has a race condition.

Two servers read the same counter at the same time. Both see capacity. Both proceed. You overshoot your quota. Every time.

BEFORE Shared counter

  • Server A and Server B both read a token count of 1
  • Both see capacity, both proceed
  • Your user makes 2 requests when they should have made 1
  • Locks help, but add latency and complexity

AFTER WaitState

  • Every check routes to a single-threaded process on one machine
  • Requests are serialized, token math is atomic
  • No locks, no coordination overhead
  • Double-spend is impossible at the architectural level

Three concepts.
One API call.

WaitState is deliberately minimal. You define buckets, your adapter computes cost, WaitState enforces the limit atomically.

01

Define a bucket

A bucket is a capacity and refill_rate. Set it once with your admin token. Pin it to a region. That's your entire config.

02

Call /v1/deduct

Pass a hashed user ID, the bucket name, and a cost you computed client-side. WaitState atomically checks and deducts from that user's isolated token bucket.

03

Get a binary answer

200 allowed: proceed. 429 with Retry-After: wait. No parsing, no state to manage on your end.

<50ms
warm latency, globally
1
API call to enforce any limit
0
race conditions, by design

Join the first wave.

We're onboarding a small group of teams to help shape the product. Drop your email and we'll be in touch.

You're on the list. We'll be in touch when we're ready for you.

No spam. We'll reach out when we're ready for you.