How to Send WhatsApp Messages from Ruby (No Gem Required)
Send WhatsApp messages from Ruby using only Net::HTTP — no gem or SDK dependency required. Session setup, QR auth, sending text/images/files, and production error handling.
Meta Description: Send WhatsApp messages from Ruby using only Net::HTTP — no gem or SDK required. Session setup, QR auth, sending text/images/files, and production error handling.
Introduction
Search "WhatsApp API Ruby" and every result reaches for a gem first — ruby_whatsapp_sdk, a vendor wrapper, or a Rails initializer wired to a Business Account. None of that is necessary. The WhatsApp Messaging API is plain REST over HTTPS, and Ruby's standard library (net/http, json) is all you need to talk to it.
In this guide, you'll send a real WhatsApp message from Ruby in under 5 minutes, with zero external dependencies — no Gemfile entry, no bundle install.
What you'll need:
- •Ruby 3.0+ (ships with
net/httpandjsonin the standard library) - •A RapidAPI account (free)
- •5 minutes
Let's send a message.
Step 1: Get Your API Key (30 seconds)
- 1.Go to RapidAPI Hub
- 2.Search for "Whatsapp messaging bot"
- 3.Click Subscribe (Free plan available, no card required)
- 4.Open API Settings and copy your X-RapidAPI-Key
That key authenticates every request below.
Step 2: Create a WhatsApp Session
Before sending a message, pair a session — this is the WhatsApp account the API sends on behalf of. Sessions authenticate the same way WhatsApp Web does: scan a QR code once, and the API keeps that connection alive.
# session.rb
require 'net/http'
require 'json'
require 'uri'
API_HOST = 'whatsapp-messaging-bot.p.rapidapi.com'
API_KEY = ENV.fetch('RAPIDAPI_KEY')
SESSION_NAME = 'my-session'
def call_api(method, path, body = nil)
uri = URI("https://#{API_HOST}#{path}")
request = case method
when :post then Net::HTTP::Post.new(uri)
when :get then Net::HTTP::Get.new(uri)
end
request['x-rapidapi-key'] = API_KEY
request['x-rapidapi-host'] = API_HOST
request['Content-Type'] = 'application/json'
request.body = body.to_json if body
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.request(request)
end
[response.code.to_i, JSON.parse(response.body || '{}')]
end
# Create the session
status, created = call_api(:post, '/v1/sessions', { name: SESSION_NAME })
puts "Session created: #{created}"
# Get the QR code to authenticate
status, qr = call_api(:get, "/v1/sessions/#{SESSION_NAME}/auth/qr")
puts "Scan this QR with WhatsApp → Linked Devices:"
puts qr['qr'] || 'QR not ready yet, retry in a second'Run it:
RAPIDAPI_KEY=your_key_here ruby session.rbOpen WhatsApp on your phone → Settings → Linked Devices → Link a Device, and scan the printed QR code.
Step 3: Send Your First Message
Once the session shows as authenticated, sending is a single request:
# send.rb
require_relative 'session' # reuses call_api() and SESSION_NAME above
def send_whatsapp_message(chat_id, text)
call_api(:post, '/v1/sendText', {
chatId: chat_id, # just the number: "14155552671"
text: text,
session: SESSION_NAME
})
end
status, result = send_whatsapp_message('14155552671', 'Hello from Ruby!')
if [200, 201].include?(status)
puts 'Message sent.'
else
puts "Send failed: #{result}"
endruby send.rbCheck your phone. That's a real WhatsApp message sent from Ruby with zero dependencies.
Step 4: Sending Images, Files, and Voice Notes
Same call_api helper, different endpoint and payload:
def send_image(chat_id, image_url, caption = '')
call_api(:post, '/v1/sendImage', {
chatId: chat_id,
image: image_url,
caption: caption,
session: SESSION_NAME
})
end
def send_file(chat_id, file_url, filename)
call_api(:post, '/v1/sendFile', {
chatId: chat_id,
file: file_url,
filename: filename,
session: SESSION_NAME
})
endBoth take a public URL — the API fetches and delivers it, no upload step needed. Full parameter reference for every message type (including voice, video, location, and contact cards) is in the interactive API reference.
Production Best Practices
1. Retry with backoff
A single failed request shouldn't fail the whole job:
def send_with_retry(chat_id, text, max_retries: 3)
status, result = nil
(1..max_retries).each do |attempt|
status, result = send_whatsapp_message(chat_id, text)
return [status, result] if status < 400
sleep(attempt * 2) # 2s, 4s, 6s backoff
end
[status, result]
end2. Reuse the HTTP connection, don't recreate sessions per request
Net::HTTP.start opens a new TCP connection per call above, which is fine for a script but wasteful in a request loop. In a Rails app or worker, wrap the block in a persistent Net::HTTP.start(...) do |http| ... end and pass http into call_api instead of opening a new one each time. Either way, the WhatsApp session itself (SESSION_NAME) is separate from the HTTP connection — create it once, store the name in ENV or a config table, and reuse it across every subsequent send.
3. Confirm delivery with a webhook, not the HTTP response
A 200 from /v1/sendText means "accepted," not "delivered" — a number that isn't on WhatsApp can still return success. Register a webhook and read the message.ack event for real delivery/read status:
call_api(:post, "/v1/sessions/#{SESSION_NAME}/webhooks", {
webhookUrl: 'https://yourapp.com/webhooks/whatsapp',
events: ['message.ack']
})Troubleshooting
| Issue | Fix |
|---|---|
| 401 Unauthorized | RAPIDAPI_KEY environment variable isn't set, or the key's been rotated in the RapidAPI dashboard. |
OpenSSL::SSL::SSLError | Older Ruby installs (via rbenv/rvm) can ship a stale CA bundle — update the openssl gem or point SSL_CERT_FILE at your system's cert store. |
| QR code expires before you scan it | QR codes are short-lived — fetch it right before you're ready to scan, not minutes ahead. |
| Message accepted but never arrives | Check message.ack via webhook — a 200 only means the API accepted the send, not that WhatsApp delivered it. |
Next Steps
- •WhatsApp OTP Authentication — reuse this same Ruby client to send login codes instead of SMS
- •Send Files, PDFs, and Documents via WhatsApp API — full MIME type reference and size limits
- •Best WhatsApp API for Startups and Indie Hackers — how this compares on price and setup time
- •Will My Number Get Banned? — the honest version of the "unofficial APIs get you banned" warning
External references:
- •Net::HTTP documentation — Ruby's official standard library docs for every method used above
- •WhatsApp Business Platform overview — Meta's official docs, for context on how the official API differs
Try Free on RapidAPI → Get your API key in 30 seconds → Send your first Ruby message in 5 minutes.
Ready to Get Started?
Try the WhatsApp API free on RapidAPI with no credit card required.
Try Free on RapidAPI