Anthropic Ruby SDK: The Official Gem, Not ruby-anthropic
The official Anthropic Ruby SDK is the gem named anthropic. Install it in Rails, then create messages, stream responses, run typed tools, and handle retries.
The official Anthropic Ruby SDK is a gem named anthropic, and for a new Ruby or Rails project talking directly to Claude it is the one I would install first. This page is setup notes, not a full agent tutorial: what the gem is, how to tell it apart from the older community gem with a similar name, how to install it, and how to make the three calls you will actually use most: create a message, stream a response, and run a tool.
If you want the full agent build (tool design, the loop, background jobs, human approval, and testing), that lives in my guide to building AI agents in Ruby with the Anthropic SDK. This page is the install-and-reference companion to it.
Scope note: the examples assume Ruby 3.2+, the anthropic 1.x gem, and the Claude API model ID claude-sonnet-5. The SDK moves quickly, so pin the gem and read the changelog before upgrading code that uses beta helpers.
What the Anthropic Ruby SDK Is
The Anthropic Ruby SDK is the official client library for the Claude API, published by Anthropic as the anthropic gem at anthropics/anthropic-sdk-ruby.
It gives you a threadsafe Anthropic::Client, a messages.create call for one-shot completions, streaming helpers for token-by-token output, typed tools defined as Ruby classes, and a tool_runner that drives the whole agent loop. It is a code-generated SDK (Ruby 3.2.0+), which is why the API surface is broad and consistent across the message, streaming, and tool paths.
Which Gem Is the Official One
The official one is anthropic, by Anthropic. The confusion is historical: the popular community gem by Alex Rudall was originally also named anthropic, and it was deliberately renamed to ruby-anthropic to hand the anthropic name over to Anthropic's official SDK. So if a tutorial from 2024 tells you to gem "anthropic" and then calls Anthropic.configure with an access_token, it is describing the old community gem under its old name. That API no longer ships as anthropic.
Here is the disambiguation, because "which anthropic ruby gem" is a common source of setup bugs:
| Gem name | Status | Maintainer | When to use it |
|---|---|---|---|
anthropic |
Official SDK, actively developed (1.x) | Anthropic | New Ruby/Rails projects; anything wanting streaming, typed tools, or the tool runner |
ruby-anthropic |
Community gem, maintained | Alex Rudall | Existing apps already built on it, or when you specifically want its lighter, hand-written API |
anthropic (pre-1.0, old name) |
Retired name | Alex Rudall | Never for new code; this name now points at the official SDK |
You may also run across an older obie/anthropic fork from the pre-official-SDK era. It is historical; new projects should ignore it and install the official gem.
The practical tell at runtime: the official SDK is instantiated with Anthropic::Client.new(api_key: ...), while the community ruby-anthropic client uses Anthropic::Client.new(access_token: ...) or a global Anthropic.configure block. Different keyword, different gem. If you paste a snippet and Ruby complains about an unknown keyword, you are almost certainly mixing the two.
Install the anthropic Gem
Add gem "anthropic" to your Gemfile, run bundle install, and set ANTHROPIC_API_KEY. That is the whole install; the SDK needs Ruby 3.2.0 or newer.
# Gemfile
gem "anthropic"
bundle install
Then make the API key available in the environment. The client reads ANTHROPIC_API_KEY by default, so you never hard-code it:
export ANTHROPIC_API_KEY="sk-ant-..."
A few things are worth pinning down before you go further. The SDK requires Ruby 3.2.0 or newer; on anything older, bundle install will refuse the gem. Pin the gem version too. The gem is on the 1.x line, and the tool runner still lives under a beta namespace, so pin anthropic to the exact version you tested in the Gemfile and read the changelog before bumping; that keeps a minor upgrade from silently changing behavior under you. And keep the key out of git: in Rails, ENV["ANTHROPIC_API_KEY"] or Rails.application.credentials both work; the client just needs the value at boot.
Initialize the Client
Create one Anthropic::Client and reuse it. The client is threadsafe and manages its own connection pool, so building a fresh one per request wastes connections for no benefit. In Rails, an initializer assigning a constant is the natural home:
# config/initializers/anthropic.rb
ANTHROPIC = Anthropic::Client.new(
api_key: ENV.fetch("ANTHROPIC_API_KEY")
)
Using ENV.fetch rather than ENV[] means the app fails loudly at boot if the key is missing, instead of failing on the first API call after deploy. From here, ANTHROPIC is the one client the whole process shares.
If you prefer not to lean on the default, you can pass the key explicitly (shown above) or configure timeouts and retry counts per client. The important part is the lifecycle: build once, share everywhere.
One operational detail matters in Rails: the client is threadsafe, but not magic. It owns an HTTP connection pool, so sharing one client per app process avoids building new pools on every request. If you fork workers after boot, initialize the client in the worker process or make sure no request is in flight across the fork.
Send Your First Message
Call messages.create with a model, a max_tokens cap, and a messages array. This is the core of the SDK, and every other feature builds on it:
message = ANTHROPIC.messages.create(
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "Summarize what a Rails initializer is in one sentence." }
]
)
# content is an array of typed blocks; the text lives on the first one.
puts message.content.first.text
Three required parameters: model, max_tokens, and messages. The response is a Message object, not a raw hash, so message.content is an array of content blocks and message.usage carries the input and output token counts you will want to log for cost tracking. Multi-turn conversations are just a longer messages array alternating role: "user" and role: "assistant".
That single call is the whole SDK in miniature. Everything below is a variation on it.
Retries, Timeouts, and Errors
Do not ship the first message call without deciding how failures behave. The SDK retries connection errors, request timeouts, 408, 409, 429, and 5xx responses by default, with a short exponential backoff. That is helpful for transient failures, but it also means a controller action can wait longer than you expect if the API is slow.
For web requests, set a shorter timeout and move longer work into a job:
ANTHROPIC = Anthropic::Client.new(
api_key: ENV.fetch("ANTHROPIC_API_KEY"),
timeout: 20,
max_retries: 2
)
The useful failure boundary is usually a background job retry, not an HTTP retry hidden inside a user request. Log Anthropic::Errors::RateLimitError separately from connection failures, because a 429 means you need backoff or quota work; a network failure usually means the next job attempt can try again unchanged.
Streaming Responses
Use messages.stream and iterate stream.text when you want tokens as they arrive instead of waiting for the full response. This is what keeps a chat UI responsive:
stream = ANTHROPIC.messages.stream(
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "Draft a two-line deploy announcement." }
]
)
stream.text.each do |chunk|
print chunk # each token as the model produces it
end
If you would rather block until the response is complete (for a background job, say, where there is no user watching), the stream exposes accumulation helpers: stream.accumulated_text returns the whole string once the stream closes, stream.accumulated_message returns the full Message object, and stream.until_done simply blocks until it finishes. Pick incremental or blocking per use case; you do not need both on the same stream.
In a Rails app this pairs cleanly with Turbo Streams or ActionCable: broadcast each chunk as it arrives and the user watches the answer type itself out.
Tool Use (Function Calling)
Define tools as Anthropic::BaseTool subclasses with a typed input schema, then either pass them to messages.create or hand them to the tool runner. A minimal tool looks like this:
# The typed input the model must fill in.
class LookupOrderInput < Anthropic::BaseModel
required :order_id, Integer, doc: "The order ID to look up"
end
class LookupOrder < Anthropic::BaseTool
doc "Look up a single order by its ID. Use when the user asks about one specific order."
input_schema LookupOrderInput
# `input` is the parsed, typed schema object.
def call(input)
Order.where(id: input.order_id)
.as_json(only: %i[id status total_cents placed_at])
end
end
The doc string is what the model reads to decide when to call the tool, so treat it as an interface description, not an afterthought. The input_schema gives you typed, validated arguments (Anthropic::EnumOf[...] constrains a field to a fixed set of values), which stops the model from inventing inputs your code cannot handle.
The SDK can then run the entire tool loop for you: call the model, execute any tool it requests, feed the result back, and repeat until it produces a final answer. That helper is beta.messages.tool_runner:
ANTHROPIC.beta.messages.tool_runner(
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "What's the status of order 5591?" }
],
tools: [LookupOrder.new]
).each_message do |message|
# Each turn streams through here: tool-use requests, results, final answer.
Rails.logger.info(message.content)
end
Note the beta.messages namespace: the tool runner is still under beta, so its shape can change between releases. That is the main reason to pin your gem version. This is only the minimal shape; tool descriptions, authorization scoping, human approval, and testing are the parts that actually matter before you ship.
Using It Inside Rails
Call the client from a service object or background job using the constant from your initializer. That constant is available everywhere, so a plain service object or background job is usually all the structure you need:
class SummaryJob < ApplicationJob
queue_as :ai
def perform(document_id)
document = Document.find(document_id)
message = ANTHROPIC.messages.create(
model: "claude-sonnet-5",
max_tokens: 512,
messages: [
{ role: "user", content: "Summarize this in three bullets:\n\n#{document.body}" }
]
)
document.update!(summary: message.content.first.text)
end
end
Two Rails-specific habits pay off immediately. First, run anything multi-turn (streaming chats, tool loops) in a background job rather than a controller action, because an agent loop makes several API round trips and will happily blow past a web request timeout and tie up a Puma worker. Second, if a tool touches your database, scope its query through the same Pundit policy or tenant scope your controllers use, so the model can never read more than the user it acts for.
For the deeper Rails patterns (background jobs, authorization, streaming to the browser, observability, and testing the loop with the API stubbed), use this page only as the SDK boundary. Then move to the Rails AI Agents hub or the full agent-building walkthrough when the decision is architecture rather than client setup.
Anthropic Ruby SDK vs Alternatives
The official gem is the default for Claude-specific API work; the alternatives make sense when the boundary changes. Here is how the options actually stack up:
| Approach | Streaming and tools | Maintenance | When to reach for it |
|---|---|---|---|
Official anthropic gem |
Built-in: streaming, typed tools, tool_runner |
Official, code-generated, frequent releases | New Claude-only features; code that wants Anthropic's API surface directly |
ruby_llm (crmne) |
Streaming, tools, thinking, structured output; one API across multiple providers | Community-maintained multi-provider framework | Multi-provider apps, or Rails chat persistence out of the box |
ruby-anthropic (alexrudall) |
Streaming and tools, hand-written API | Community, maintained | Existing apps already on it; a lighter surface |
| Raw HTTP (Faraday/Net::HTTP) | You build everything yourself | You own it entirely | Extreme dependency minimalism, or an unusual proxy setup |
| No SDK (other providers) | N/A for Claude | N/A | When a provider ships no official Ruby SDK at all, as with Gemini in Ruby |
My read: reach for the raw-HTTP path only when you have a specific reason to avoid the dependency, because you will end up reimplementing streaming parsing and the tool loop by hand, and both are easy to get subtly wrong. The official SDK is the low-risk choice, and it is where new features land first.
When Not to Reach for This Gem
The anthropic gem is the wrong starting point in a few cases. If you are calling a different provider (OpenAI, Gemini, a local model), this gem does not help you; it is Claude-only, and a multi-provider app is better served by ruby_llm - I compare the two in detail in Anthropic Ruby SDK vs ruby_llm. If you have an existing app already built on ruby-anthropic and it works, migrating to the official SDK is a rewrite of your client code for no new capability, so weigh that against the maintenance benefit rather than switching reflexively. And if you truly need zero third-party gems, a thin Faraday wrapper is defensible, as long as you accept that you now own streaming and retries yourself. For everything else, install anthropic and move on.
Start With One Message
Install the gem, set ANTHROPIC_API_KEY, build one Anthropic::Client in an initializer, and get a single messages.create call returning text before you add anything else. Streaming, tools, and the tool runner are all variations on that one call, and confirming the simplest version works first saves you from debugging the SDK and your agent logic at the same time.
If the decision is SDK setup, stop here. If the decision is agent architecture, the building AI agents in Ruby guide picks up with the loop, tool classes, approval gates, and background jobs.
Related Decisions
- Rails AI Agents with the Anthropic SDK - the full agent build: tools, the loop, background jobs, human approval, and testing
- Rails AI Agents - the hub for choosing the SDK boundary, tool design, background jobs, approvals, and cost tracking
- Anthropic Ruby SDK vs ruby_llm - the full comparison: feature coverage, gaps, and when each gem wins
- Claude Agent SDK in Ruby - the higher-level agent framework, and how it differs from the raw SDK
- anthropic-sdk-ruby on GitHub - the official gem, README, and runnable examples
- The anthropic gem on RubyGems - version history and install page