Open-source project
ExHammer/hammer avatar
ExHammer/hammer

hammer: an Elixir rate limiter where the limit lives next to the check

An Elixir rate-limiter with pluggable backends

922 stars49 forksElixirMIT

At a glance

What is it?
Hammer is a rate limiter for Elixir with pluggable storage backends, ETS by default and Redis or Mnesia as separate packages, and it ships a fixed window, leaky bucket, token bucket and sliding window set of algorithms. There is no global configuration: each limit is declared where it is enforced, which is a deliberate departure from how most limiters are wired.
Who is it for?
hammer fits a Phoenix or plain Elixir application that needs per-user or global limits on API requests or uploads, that wants to swap the storage backend without rewriting the limiter logic, and that is comfortable expressing each limit in the module that enforces it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Elixir, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

No global configuration, limits declared where they are checked

Hammer is described as a rate limiter for Elixir with pluggable storage backends, letting you set limits on actions performed within specified time intervals and apply per-user or global limits on API requests, file uploads and similar operations. The design decision that sets it apart from most limiters is stated without hedging in the README: Hammer has no global configuration, each limit is defined where it is checked, with a call of the form MyApp.RateLimit.hit(key, scale, limit). There is no config directory entry listing quotas, no application environment block, and no central table of limits. Instead you write a module, for example MyApp.RateLimit, that holds the hit calls for that particular action, and your Plug calls into it. The consequence for a team is that limits live in the codebase next to the code that enforces them, so a reviewer sees the quota in the same diff as the endpoint, and there is no separate file to drift out of sync. The cost is that there is no single place to answer a question like which limits exist across the system, and the Tutorial in the docs is where the guidance on composing keys and using the limiter as a plug lives.

Four backends, and only two of them are shared across nodes

The README distinguishes atomic backends, which are single-node rate limiting but will be the fastest option, from backends that can be shared. Hammer.ETS is the default and the README notes it can be distributed, with a guide for that in the repository. Hammer.Atomic is the other single-node option, built on the Elixir atomics primitives, and it is the fastest of the set. Hammer.Redis and Hammer.Mnesia live in their own repositories, hammer-backend-redis and hammer-backend-mnesia, which is a packaging choice with an operational benefit: a team already running Redis adds one dependency rather than taking on the whole library, and the Redis package also brings its own algorithm set. That separation also means the version numbers of the backend packages move independently of Hammer itself, so a project with several backends in play has to track two version streams. For a single-node service, the default ETS backend is the simplest choice and the fastest; the moment you run more than one node and want a limit to be global, you are choosing Redis or Mnesia and taking on the operational dependency that implies.

The algorithm table is the decision you actually make

Each backend supports multiple algorithms, and the README is explicit that not all of them are available for all backends, then gives a full table. Hammer.Atomic offers FixWindow, FixWindowPerKey, LeakyBucket and TokenBucket. Hammer.ETS offers the same four plus SlidingWindow, which the atomic backend does not have. Hammer.Redis offers FixedWindow, LeakyBucket and TokenBucket, and Hammer.Mnesia offers the same three. The README's comparison section describes the choices in terms of what you are optimising for. Fixed Window is the simplest implementation with the lowest overhead, good for basic limits with clear time boundaries, and its edge case is that up to twice the allowed requests can pass at a window boundary. Fixed Window Per Key keeps the same low overhead and one-entry-per-key footprint, but each key's window is anchored to its first hit rather than to a globally aligned wall clock, so boundaries are not synchronised across keys and cannot be exploited deterministically; the README notes it has the same semantics as the common Redis INCR plus EXPIRE NX pattern. Leaky Bucket provides a smooth, consistent rate as requests leak out at a constant rate, which suits traffic shaping and queue processing. Token Bucket allows controlled bursts while maintaining an average rate.

The default fixed window lets you double your rate at the edge

By default, Hammer backends use the fixed window counter, tracking actions within set time windows and resetting the count at the start of each new window. The README spells out the consequence with a concrete example: with a limit of 10 uploads per minute, a user could upload up to 10 files between 12:00:00 and 12:00:59 and up to 10 more between 12:01:00 and 12:01:59, and a user can upload 20 videos in a second if the uploads are timed at the window edges. That is the classic fixed window burst, and the README does not hide it. It also gives the workaround, and this is the part worth remembering: implement a bursty counter using the current API by making two checks, one for the original interval with the total limit, and one for a shorter interval with a fraction of the limit. Smoothing the allowed rate that way does not require a different algorithm, it requires two hit calls. If you choose Token Bucket or Leaky Bucket instead, the burst behaviour changes shape rather than disappearing, so the choice is about which failure mode you prefer to reason about.

Adding Hammer to a Mix project, with or without Igniter

Hammer is published on Hex and is added to the dependencies list in mix.exs, with the README showing the dependency entry as {:hammer, "~> 7.0"}. If you use Igniter, a single command adds the dependency, generates a MyApp.RateLimit module and adds it to your application's supervision tree:

bash
mix igniter.install hammer

Pass --backend atomic or --backend redis to pick a backend other than the default :ets, and the redis option also adds the hammer_backend_redis dependency. If Hammer is already in the project, the command to run is mix hammer.install. The generated module and the supervision tree entry are the two pieces of wiring you would otherwise write by hand, and the Igniter path exists because that wiring is the part every project repeats. The README also notes that the atomic backends are the fastest option and are single-node, which is the constraint to keep in mind when you choose a backend in the installer, because the installer will not ask whether you run one node or several.

What the repository documents and what it does not

The tree shows a mature package layout: a mix.exs and mix.lock, a lib directory, a test directory, a bench directory, guides for topics such as distributed ETS, an assets directory, a BENCHMARKS.md, a CHANGELOG.md, a RELEASE.md and a CREDITS.md, plus AGENTS.md, CLAUDE.md and a.credo.exs for static analysis and a.formatter.exs for formatting. The package is on Hex with a documentation badge pointing at hexdocs.pm, and the topic tags place it squarely in the Phoenix ecosystem. Two gaps are worth naming. There is no GitHub release history for the project, so version tracking happens on Hex and in the changelog rather than in tagged releases, and the README does not document what happens to counters when the backing store is unreachable, which is the failure case that decides whether your limit fails open or fails closed in production. The last push was on 2026-10-21, and the project is MIT licensed with the licence file in the repository root.

Editorial conclusion

hammer fits a Phoenix or plain Elixir application that needs per-user or global limits on API requests or uploads, that wants to swap the storage backend without rewriting the limiter logic, and that is comfortable expressing each limit in the module that enforces it. It does not fit a multi-node deployment that assumes a shared limit store without choosing the Redis or Mnesia backend, and it does not fit a team that wants a single global configuration file defining every quota, because the library has no global configuration at all. Verify first which algorithm and backend combination you need, since the README states that not every algorithm is available for every backend, and confirm the distributed ETS guide applies to your topology. The licence field on the repository says MIT, and the last push was on 2026-10-21.

Frequently asked questions

What is hammer and how is it configured?

Hammer is a rate limiter for Elixir with pluggable storage backends, used to set limits on actions within time intervals, per user or globally. It has no global configuration: each limit is defined where it is checked, with a call such as MyApp.RateLimit.hit(key, scale, limit).

Which storage backends does hammer support?

Hammer.ETS is the default and can be distributed, Hammer.Atomic is the fastest and is single-node, and Hammer.Redis and Hammer.Mnesia are shipped as separate packages in their own repositories. The README says atomic backends are single-node rate limiting but will be the fastest option.

How do I install hammer in an Elixir project?

Add {:hammer, "~> 7.0"} to the dependencies in mix.exs, or use Igniter with mix igniter.install hammer, which adds the dependency, generates a MyApp.RateLimit module and adds it to the supervision tree. You can pass --backend atomic or --backend redis to choose a backend other than the default :ets.

What is the default rate limiting algorithm in hammer?

The fixed window counter, which resets the count at the start of each new window. The README notes that with a limit of 10 uploads per minute a user could upload 20 videos in a second if the uploads are timed at the window edges, and suggests a bursty counter implemented with two checks as the workaround.

Under which licence is hammer released?

The licence field on the repository says MIT, and the README links the MIT licence file in the repository root.

Official sources

  1. ExHammer/hammer on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/exhammer-hammer.svg)](https://hysenlabs.com/projects/exhammer-hammer)

Was this useful?