Claude API Ruby Example

Working Ruby code for the Claude API using net/http. No official Ruby gem needed — call Claude from Rails, Sinatra, or plain Ruby scripts with these copy-paste examples.

💥 50p impulse-buy: Power Prompts PDF (first 10 buyers) 30 battle-tested Claude Code prompts · 8-page PDF · paste into CLAUDE.md and never re-type a prompt again · 50p impulse-buy, no commitment

Anthropic has no official Ruby gem as of 2026, so the idiomatic approach is to call the REST API directly via Ruby's built-in net/http. These examples work in plain Ruby scripts, Rails, and Sinatra.

Minimal example

require 'net/http'
require 'json'
require 'uri'

def claude(prompt, model: "claude-sonnet-4-6", max_tokens: 1024)
  uri = URI("https://api.anthropic.com/v1/messages")
  req = Net::HTTP::Post.new(uri, {
    "x-api-key"         => ENV["ANTHROPIC_API_KEY"],
    "anthropic-version" => "2023-06-01",
    "content-type"      => "application/json"
  })
  req.body = JSON.generate({
    model:      model,
    max_tokens: max_tokens,
    messages:   [{ role: "user", content: prompt }]
  })

  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
  JSON.parse(res.body).dig("content", 0, "text")
end

puts claude("Explain Ruby blocks in one paragraph.")

With system prompt

req.body = JSON.generate({
  model:      "claude-sonnet-4-6",
  max_tokens: 512,
  system:     "You are a concise Ruby expert. Answer in bullet points.",
  messages:   [{ role: "user", content: "What is the difference between Proc and Lambda?" }]
})

Reusable ClaudeClient class

require 'net/http'
require 'json'
require 'uri'

class ClaudeClient
  BASE_URI = URI("https://api.anthropic.com/v1/messages").freeze

  def initialize(api_key: ENV["ANTHROPIC_API_KEY"], model: "claude-sonnet-4-6")
    @api_key = api_key
    @model   = model
  end

  def chat(messages, system: nil, max_tokens: 1024)
    body = { model: @model, max_tokens: max_tokens, messages: messages }
    body[:system] = system if system

    req = Net::HTTP::Post.new(BASE_URI, headers)
    req.body = JSON.generate(body)

    res = Net::HTTP.start(BASE_URI.hostname, BASE_URI.port, use_ssl: true) { |h| h.request(req) }
    raise "Claude error #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)

    JSON.parse(res.body).dig("content", 0, "text")
  end

  private

  def headers
    {
      "x-api-key"         => @api_key,
      "anthropic-version" => "2023-06-01",
      "content-type"      => "application/json"
    }
  end
end

client = ClaudeClient.new
puts client.chat([{ role: "user", content: "Write a Ruby one-liner that reverses each word in a string." }])

Streaming response

require 'net/http'
require 'json'
require 'uri'

uri = URI("https://api.anthropic.com/v1/messages")

Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  req = Net::HTTP::Post.new(uri, {
    "x-api-key"         => ENV["ANTHROPIC_API_KEY"],
    "anthropic-version" => "2023-06-01",
    "content-type"      => "application/json"
  })
  req.body = JSON.generate({
    model:      "claude-sonnet-4-6",
    max_tokens: 1024,
    stream:     true,
    messages:   [{ role: "user", content: "Explain metaprogramming in Ruby." }]
  })

  http.request(req) do |res|
    buffer = ""
    res.read_body do |chunk|
      buffer += chunk
      while (line = buffer.slice!(/^data: .+
/))
        data = line.sub("data: ", "").strip
        next if data == "[DONE]"
        event = JSON.parse(data) rescue next
        if event.dig("delta", "type") == "text_delta"
          print event.dig("delta", "text")
          $stdout.flush
        end
      end
    end
  end
end
puts

Error handling with retry

def claude_with_retry(prompt, retries: 3)
  delay = 1
  retries.times do |attempt|
    result = claude(prompt)
    return result
  rescue => e
    raise e if attempt == retries - 1
    sleep delay
    delay *= 2
  end
end

Rails integration

# config/initializers/claude.rb
require_relative "../../lib/claude_client"
CLAUDE = ClaudeClient.new(api_key: Rails.application.credentials.anthropic_api_key)

# app/controllers/chat_controller.rb
class ChatController < ApplicationController
  def create
    reply = CLAUDE.chat([{ role: "user", content: params[:message] }])
    render json: { reply: reply }
  end
end

Ruby vs Python SDK comparison

AspectRuby (net/http)Python (anthropic SDK)
Official packageNone — use net/httppip install anthropic
StreamingManual SSE parsingclient.messages.stream() context manager
Retry / backoffManualBuilt-in via SDK
Rails integrationService object or initializerN/A
AsyncRactors / async-http gemAsyncAnthropic class

To estimate token costs for your Ruby app, use the Claude API Cost Calculator. For pricing details, see the Anthropic API pricing page.

Frequently asked questions

Is there an official Anthropic Ruby gem?
Not as of mid-2026. Use Ruby's built-in `net/http` with `json` to call the REST API directly. The examples below are the canonical approach.
How do I set my API key in Ruby?
Use `ENV['ANTHROPIC_API_KEY']` in your code and set it via `export ANTHROPIC_API_KEY=sk-ant-...` in your shell, or via Rails credentials / dotenv in a Rails app.
Can I stream responses in Ruby?
Yes — use `Net::HTTP.start` with a block and set `response.read_body` with `stream_body: true`. The response comes as SSE chunks you parse with a simple line buffer.
Which Claude model should I use in Ruby?
Use `claude-sonnet-4-6` for most tasks. Use `claude-haiku-4-5-20251001` for high-volume cheap requests. Use `claude-opus-4-7` only for tasks requiring maximum reasoning.
Does this work in Ruby on Rails?
Yes. Add the code to an initializer or service object. Use `Faraday` with the `faraday-net_http` adapter if you prefer a gem-based HTTP client — the pattern is identical.

Free tools

Cost Calculator → API Cookbook → Diff Summarizer → Skills Browser →

More examples

Claude API Python QuickstartClaude API Node.js / TypeScript QuickstartClaude API Streaming in PythonClaude API Streaming in Node.js / TypeScriptClaude API Tool Use in PythonClaude API Tool Use in Node.js / TypeScript