Migrate to RunxBuild today!! Deploy More while Spending Less

Calculate your savings
unxBuild
Back to Blog Explainer

Redis SCAN and the COUNT Option: What It Does and What It Does Not

Sean

Platform Writer

Oct 15, 2026
7 min read

The COUNT option on Redis SCAN is a hint about how much work to do per iteration, not a guarantee of how many keys you get back - SCAN is a cursor-based iterator that may return any number of keys per call, including zero, and you must keep calling until the cursor returns to 0 to be sure you have seen everything. Understanding that it is a hint, not a limit, is the difference between code that works and code that silently misses keys.

Redis SCAN and the COUNT Option: What It Does and What It Does Not

People reach for SCAN COUNT expecting it to mean give me this many keys. It does not, and that misunderstanding is behind most SCAN bugs.

Table of contents

Why SCAN exists instead of KEYS

The naive way to list keys is KEYS pattern, and it is a trap in production: KEYS scans the entire keyspace in one blocking operation, and on a large database it freezes the server for everyone while it runs. On anything real, that is an outage.

SCAN was built to avoid exactly that. It is a cursor-based iterator: each call does a small, bounded amount of work and returns a cursor plus whatever keys it found. You feed the cursor back on the next call. The server is never blocked for long, so other clients keep working. The rule is simple: never use KEYS on a production database; use SCAN.

What COUNT actually controls

COUNT tells Redis roughly how much work to do in one iteration - how many elements to examine internally - not how many keys to return. With a small COUNT, each call is quick but you make more calls. With a large COUNT, each call does more work and returns more at once but takes longer.

Crucially, a single SCAN call can return more keys than COUNT, fewer than COUNT, or none at all, and still have more to find. The number of keys returned is not controlled by COUNT. It is a knob for the cost-per-iteration trade-off, defaulting to 10, and you tune it for throughput versus latency, not to set a batch size you can rely on.

Iterate until the cursor is zero

The correct pattern is a loop that continues until SCAN returns a cursor of 0, accumulating keys across every call. A single call is never guaranteed to find every match - even with MATCH or a large COUNT - so stopping early silently misses keys.

cursor = 0
keys = []
while True:
    cursor, batch = r.scan(cursor=cursor, match='user:*', count=500)
    keys.extend(batch)
    if cursor == 0:
        break

The cursor returning to 0 is the only reliable signal that iteration is complete. MATCH filters after elements are fetched, so most iterations with a selective pattern return nothing - that is expected, not a bug, and it is exactly why you must loop to the end.

Tuning COUNT in practice

Because COUNT trades per-call cost against number of calls, the right value depends on your situation. A low COUNT keeps each call fast and the server responsive, at the price of many round trips - good when latency to other clients matters most. A high COUNT (say 1000) finishes the full scan in fewer calls, useful for a background sweep where total time matters more than per-call latency.

You are allowed to change COUNT between iterations of the same scan, as long as you keep passing the cursor through. The same options - COUNT, MATCH, TYPE - apply to the type-specific variants SSCAN, HSCAN, and ZSCAN for scanning within a set, hash, or sorted set.

Where RunxBuild fits

RunxBuild offers managed MySQL and Postgres as databases, not managed Redis, so if your architecture uses Redis you would run it yourself or through a Redis-specific provider - this post is a neutral explainer, not a pitch to host Redis with us.

Where RunxBuild does fit is the application that talks to Redis: a Python, Node, or other service that uses a cache is still an app we deploy from a GitHub repo, with environment variables for the Redis connection string, runtime logs, and autoscaling. The cache lives wherever you run it; the service that uses it, and the managed Postgres or MySQL beside it, is the part RunxBuild runs.

How this fits the rest of the stack

The cost of a project is the sum of its line items, not the headline number, and the RunxBuild hosting calculator is the right place to add them up: the service, the database, the storage, the bandwidth, each a separate figure. Model it before committing, not after the first invoice. The RunxBuild dashboard is where the running project shows its real usage in one place.

Useful related references:

FAQ

Does Redis SCAN COUNT limit how many keys I get?

No. COUNT is a hint about how much work Redis does per iteration internally, not a limit on returned keys. A single SCAN call can return more, fewer, or zero keys regardless of COUNT, and still have more to find, so you cannot treat it as a reliable batch size.

Why not just use KEYS in Redis?

KEYS scans the entire keyspace in one blocking operation, freezing the server for all clients while it runs - on a large production database that is effectively an outage. SCAN iterates in small bounded steps without blocking, so other clients keep working, which is why KEYS should never run in production.

How do I make sure SCAN returns all matching keys?

Loop until SCAN returns a cursor of 0, accumulating keys from every call. A single call is never guaranteed to find every match even with MATCH or a large COUNT, so the cursor returning to 0 is the only reliable signal that iteration is complete.

Why does SCAN sometimes return no keys?

Because MATCH filters elements after they are fetched, so an iteration that examines keys which do not match the pattern returns nothing - while still advancing the cursor. This is normal, not a bug, and it is exactly why you must keep iterating until the cursor reaches 0 rather than stopping at the first empty result.

What COUNT value should I use with SCAN?

It depends on the trade-off: a low COUNT keeps each call fast and the server responsive but needs more round trips, while a high value like 1000 finishes a full scan in fewer calls, better for background sweeps. You can change COUNT between iterations as long as you keep passing the cursor through.

#redis#scan#count#database#cursor