Gibbon turns MailChimp API calls into Ruby method chains
Gibbon is an API wrapper for MailChimp's API
At a glance
- What is it?
- Gibbon is a MIT licensed Ruby wrapper around the MailChimp marketing API that builds request paths from method calls and maps five verbs onto HTTP methods. It suits Ruby and Rails teams who want a thin client without hand written URLs, and it comes with one sharp edge, an instance that stays dirty until a verb runs.
- Who is it for?
- Gibbon fits Ruby and Rails services that touch MailChimp occasionally, especially where the codebase wants a small client and a readable call chain rather than a large SDK. Skip it if your team wants typed resources and generated documentation, or if a single instance is shared across threads, because the documented path reset rule makes shared state a bug source.
- 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 90 days ago.
- What is it written in?
- Mainly Ruby, 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
Unknown method calls become path segments
The mechanism behind Gibbon is unusual enough to be worth stating plainly: it defines no methods for MailChimp resources at all. Any unrecognized method call appends a segment to an internal path and returns the same object, so the chain is what carries the resource structure. A call such as `gibbon.lists(list_id).members.retrieve` builds the path `lists/<list_id>/members` and issues a `GET`.
That means the library has no resource list to keep in sync with MailChimp. The documentation says MailChimp's own resource documentation is the list of available resources, and the chain mirrors the path structure of the REST API directly. Resource IDs are supplied inline, which means a list id, a campaign id or a subscriber hash sit right in the call rather than in a separate options object.
The trade-off is a direct one. You get the whole API surface for free, including endpoints Gibbon has never heard of, but you also get no compile time check that a path is valid. A typo in a segment name becomes a 404 from MailChimp rather than an error at boot.
Five verbs decide the HTTP method and whether a body is allowed
A chain is not a request until it ends in a CRUD verb, and the verb determines both the HTTP method and whether a `body` argument is even accepted. `create` maps to `POST`, `retrieve` (or `get`) to `GET`, `update` to `PATCH`, `upsert` to `PUT`, and `delete` to `DELETE`. `body` is supported on `create`, `update` and `upsert` only.
`upsert` is the one that has no direct HTTP twin. As described, it updates a record if it exists and inserts it otherwise, where MailChimp's API supports that behaviour.
Headers, params and body can be passed to any CRUD call, and the documentation shows a retrieve with a custom header and a query parameter:
gibbon.lists.retrieve(headers: {"SomeHeader": "SomeHeaderValue"}, params: {"query_param": "query_param_value"})The wrapper is thin by design. It builds the URL, attaches the key, sends the request and parses the reply. Anything else, such as pagination handling or error mapping, is not described in the documentation, so treat that as work your application still owns.
The instance stays dirty until a verb runs
This is the sharpest constraint in the library, and the documentation states it without softening. A request instance is reusable only after a call chain is terminated with a verb, because the verb makes the request and resets the path. The path does not reset when a chain goes out of scope.
The documented failure looks like this:
gibbon = Gibbon::Request.new(api_key: "your_api_key")
gibbon.lists # no verb, so nothing is sent and nothing is reset
gibbon.campaigns.retrieve # GET lists/campaigns -- not what you wantedThe second call is sent to `lists/campaigns` because the abandoned `lists` segment was still there. The guidance is to use a fresh instance per chain unless you know the previous one ended in a verb, and to only reuse instances after terminating a call with a verb.
Two further quirks live in the same area. Underscores in a method name become hyphens in the path, because hyphens are not valid Ruby method names, so `delete_permanent` maps to the segment `delete-permanent`. And `send` is special cased: `Object#send` would otherwise shadow the segment, so `Gibbon::Request#send` falls through to path building when it is called with no arguments, which is what lets a campaign be sent with `gibbon.campaigns(campaign_id).actions.send.create`. Called with arguments, `send` keeps its normal Ruby meaning and invokes the named method.
Responses are a body hash and lower case headers, and empty bodies return nil
Every CRUD verb returns a `Gibbon::Response` with two attributes. `body` holds the parsed JSON response as a `Hash`, or an `Array` depending on the endpoint. `headers` holds the response headers keyed by lower case string. The documentation shows the shape directly:
response = gibbon.lists.retrieve
response.body["lists"] # => [{"id" => "...", "name" => "...", ...}, ...]
response.headers["content-type"] # => "application/json; charset=utf-8"`body` respects the `symbolize_keys` setting, while `headers` keys are always strings. That asymmetry matters if you write code that treats both hashes the same way, so keep the distinction in mind when switching `symbolize_keys` on for one request.
There is one trap around empty responses. A response with an empty body returns `nil` rather than a `Gibbon::Response`, and the documentation points at a successful `delete` as the case, since MailChimp answers that with `204 No Content`. Code that calls `response.body` on the result of a delete will raise, and the fix is to nil check the result rather than to assume a response object always comes back.
Installing and making the first call
Gibbon is a normal gem, so installation is either a direct install or a line in the Gemfile. The direct form is:
$ gem install gibbonAnd in a bundle:
gem "gibbon"The requirements are explicit. Ruby 3.1 or newer is needed, and the documentation notes that Gibbon 4.0 dropped support for Ruby 2.4 through 3.0. You also need a MailChimp account and an API key, which the docs link to the MailChimp admin page. Under the hood the gem depends on Faraday, with both 1.x and 2.x supported, and on MultiJson. Connections negotiate TLS 1.2 or newer.
The first call starts with a one time use instance:
gibbon = Gibbon::Request.new(api_key: "your_api_key")If the key is set globally or through the `MAILCHIMP_API_KEY` environment variable, the key argument can be left off, and calls can be made on the class itself:
Gibbon::Request.lists.retrieveGlobal settings are also available for `api_key`, `timeout`, `open_timeout`, `faraday_adapter`, `proxy`, `symbolize_keys`, `logger` and `debug`. Per instance, `timeout` and `open_timeout` follow Net::HTTP semantics:
gibbon.timeout = 30
gibbon.open_timeout = 30The documentation suggests putting global values in an initializer file in a Rails app, for example `config/initializers/gibbon.rb`.
Debug output, custom loggers and where Gibbon stops being the right tool
Turning on debug logging sends output to STDOUT, and the logger can be swapped, per instance or globally, since Ruby `Logger.new` is the default:
gibbon = Gibbon::Request.new(api_key: "your_api_key", debug: true, logger: MyLogger.new)Debug logging to STDOUT deserves a note for Rails deployments, where STDOUT is where a container collects output, so a debug flag left on in production will print request data into your logs.
The real boundary is the shared instance problem. Because the path survives an abandoned chain, and because the documentation says the objects are light weight and stateful, an instance shared between threads is unsafe by design. That pushes teams toward one instance per request, which is the pattern the documentation pushes anyway.
The alternative approach is to skip the wrapper and call the MailChimp API yourself over Faraday, which Gibbon already depends on. The difference in approach is thin client versus no client: with Gibbon you get path building, key handling and JSON parsing for free, but you inherit the verb discipline and the reset rule. With raw Faraday you own URL construction and error handling, but you have no shared mutable state. For a one off integration, the raw call is often less surprising; for a service that talks to MailChimp from many places, the chain syntax pays for itself.
On maintenance, the repository is not archived and the last push was on 2026-07-25, but it has no GitHub releases, so version tracking runs through the CHANGELOG.md file that the documentation points to, along with an Upgrading section for breaking changes. The licence is MIT.
Editorial conclusion
Gibbon fits Ruby and Rails services that touch MailChimp occasionally, especially where the codebase wants a small client and a readable call chain rather than a large SDK. Skip it if your team wants typed resources and generated documentation, or if a single instance is shared across threads, because the documented path reset rule makes shared state a bug source. Before adopting, confirm the Ruby version on your servers is 3.1 or newer, because Gibbon 4.0 dropped support for 2.4 through 3.0, and check CHANGELOG.md for the breaking changes the documentation points to in its Upgrading section.
Frequently asked questions
How do I install Gibbon for the MailChimp API?
Run `gem install gibbon`, or add `gem "gibbon"` to your Gemfile. Gibbon needs Ruby 3.1 or newer, since Gibbon 4.0 dropped support for Ruby 2.4 through 3.0, plus a MailChimp account and API key.
Why does my Gibbon call hit the wrong URL?
A request instance resets its internal path only when a CRUD verb runs, so an abandoned chain leaves its segments behind. The documentation recommends a fresh instance per chain unless the previous one ended in a verb.
What does Gibbon return from a delete call?
A response with an empty body returns `nil` rather than a `Gibbon::Response`, and a successful delete answered with `204 No Content` is the documented case, so check for nil before reading `body`.
How do I send a MailChimp campaign with Gibbon?
`send` is special cased and falls through to path building when called with no arguments, so `gibbon.campaigns(campaign_id).actions.send.create` posts to `campaigns/<campaign_id>/actions/send`.
Can Gibbon read the MailChimp API key from an environment variable?
Yes. If you set the `MAILCHIMP_API_KEY` environment variable, Gibbon uses it when you create an instance, so `Gibbon::Request.new` works without passing the key.
Official sources
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.
[](https://hysenlabs.com/projects/amro-gibbon)Was this useful?