← All posts
Engineering

How we poll Reddit every 60 seconds without hitting rate limits.

An engineering note on the polling architecture behind Subportly. Adaptive intervals, sharing polls across customers, and the one trick that saved us from a 4-figure monthly API bill.

Reddit doesn’t have webhooks. Pretending it does is the most expensive thing you can do in this product.

When we started Subportly, the polling architecture took a rough sketch on a whiteboard and an afternoon of code. The naive version worked for a single test account.

It did not work for the hundredth, and the gap between “works for one” and “works for one hundred” is the most interesting engineering problem in the product. Here is how we closed it.

Constraints

Three constraints set the design space.

1. Reddit’s commercial API has rate limits per OAuth client. Roughly 600 requests per minute as of early 2026, with bursting allowed but penalized if sustained. This is generous for a single account. It is not generous for a service polling thousands of accounts in parallel.

2. There are no webhooks. Reddit will not push events to us. Every “new DM” detection has to come from a poll.

3. Customers expect new messages within ~1 minute. The product loses its punch if a DM sits for 5 minutes before showing up in their queue. The whole point is fast triage.

So the question becomes: how do you poll thousands of accounts every minute, across multiple endpoints each, without exhausting your rate-limit budget or burning through your API agreement?

The naive version

Every account, every minute, hit four endpoints (DMs, modmail, mentions, comment replies). With 100 customers averaging 3 accounts each, that’s 300 accounts × 4 endpoints × 60 polls per hour = 72,000 calls per hour, or 1,200 per minute.

This breaks two constraints. It exceeds rate limits. It also produces a giant API bill, because almost all of those calls return zero new items. We were paying for empty responses.

This was version one. It worked for a few weeks. It did not survive the first growth curve.

The four moves that fixed it

1. Adaptive polling intervals based on account activity

Most accounts are quiet most of the time. A brand account at a 5,000-customer SaaS company gets maybe 2-5 messages a day. Polling that account every 60 seconds is wasting 99% of the calls.

We instrumented every poll with “did this return anything new?” After two weeks of data, the picture was clear: account activity is bimodal. Accounts are either busy (multiple messages a day) or quiet (one message a week). The middle is rare.

So we made polling adaptive:

This alone cut our poll volume by 70%, with no perceptible change in customer-facing latency.

2. Endpoint-specific polling cadences

The four endpoints don’t all need the same cadence.

We split the four endpoints into separate poller pools, each with its own cadence rules. This gave us another ~25% reduction in volume by deferring the lower-priority endpoints.

3. Shared polling for shared subreddits

This is the trick that mattered most.

When Subportly tracks “every mention of $brand on Reddit,” what we are actually doing is monitoring the subreddits where that brand’s customers hang out. Many of our customers care about the same subreddits. Multiple SaaS brands all track r/SaaS. Multiple e-commerce brands all track r/ecommerce.

Polling those subreddits separately for each customer was redundant. We rewrote the mention-tracking layer to poll each subreddit once per cadence, then fan out matches to whichever customers cared about which keywords.

The numbers: this cut our subreddit-monitoring polls by 60% in the first week. By the time we had a few hundred customers, the savings were dramatic.

4. Backoff and circuit breakers

When Reddit’s API has a hiccup, every poll starts failing simultaneously. The naive version of polling retries immediately, which makes the API hiccup worse. The disciplined version backs off.

We use exponential backoff with jitter at the per-account level (one account failing doesn’t pause everyone), and a global circuit breaker that triggers if more than 10% of polls fail in a 30-second window (because that means Reddit, not us, is having a bad time).

The backoff means we don’t dogpile failures. The circuit breaker means we recover gracefully when Reddit recovers. Neither is novel infrastructure. Both are missing from naive implementations.

The architecture

The end result is a polling system that:

The whole thing runs on Fly.io across two regions (US-east primary, EU-west secondary for redundancy). The persistent state lives in Postgres with a Redis layer for the per-account “last seen ID” tracking that drives polling.

What we did not do

For completeness.

We did not use Reddit’s “stream” features. Reddit’s PRAW library has a stream interface that abstracts polling. Under the hood it polls. We rejected it because we needed control over the cadence to do the adaptive logic.

We did not build a “real-time” claim. The product latency is 60-90 seconds for new messages. We could chase 30 seconds with more aggressive polling, but the marginal value is zero (no human’s eyes are on the inbox at 30-second resolution) and the API cost would double.

We did not try to do this with scrapers. Several “Reddit dashboard” startups went the scraping route. They are mostly dead now. The web-scraping path is unsustainable, against terms of service, and architecturally fragile.

We did not over-engineer the dedup logic. The simple version (track last-seen item ID per account, only return items newer than that ID) handled 99% of cases. The remaining 1% (deleted-then-restored items, edits, weird modmail re-orderings) we handled in the application layer, not in the poller.

What this enables

This is what makes the rest of the product feel real.

A customer in our beta watching their inbox sees a new DM appear about 70 seconds after it lands on Reddit. That feels real-time enough to act on. A customer who logs in once a day sees the entire backlog, every account, perfectly synced, with read state preserved across sessions. None of that is glamorous infrastructure. All of it has to be there.

The polling system is something like 15% of our backend code by line count and probably 50% of the actual time we spent on backend engineering. That ratio sounds wrong until you build it.

If you are doing this in-house

Two pieces of advice.

Start with the adaptive intervals from day one. The naive uniform-polling version works fine for the first few accounts and then gets very expensive very fast. Build the adaptive logic before you scale, not after the bill arrives.

Get the rate-limit observability in early. You need to know, in real time, how close you are to your quota. We use Prometheus metrics on every poll and have a Grafana panel that shows our quota burn rate. Without that, you are flying blind.

The rest of the engineering is secondary. The polling architecture is the engineering project. Get it right, and the product on top of it feels like magic. Get it wrong, and no amount of UI polish will save you.


Subportly handles all of this so you don’t have to. One inbox, every account, real-time enough to act on. See how it works.

Keep reading