Describe a task in plain English. Meerkat runs it async — results POST to your webhook.
meerkat-agents
Official Ruby client for Meerkat — an open source, webhook-native API for async agent tasks.
Install as meerkat-agents, require as meerkat:
gem "meerkat-agents"
# ...
require "meerkat"
About Meerkat
Meerkat is open source infrastructure for async agent tasks. You describe work in plain English, connect your own LLM key (BYOK), and Meerkat schedules execution and POSTs signed JSON to your webhook — without building schedulers, scrapers, or retry logic.
| Resource | Link |
|---|---|
| Website | meerkatagents.com |
| Documentation | meerkatagents.com/docs |
| Use cases | Package tracking · Site monitoring · Agent webhooks |
| Developer guides | meerkatagents.com/blog |
| Sign up (Cloud) | cloud.meerkatagents.com/signup |
| Open source server | github.com/Tiny-Bubble-Company/meerkat |
This repo is the official Ruby gem (meerkat-agents on RubyGems). For product overview, use cases, and self-host vs Cloud, read more on the Meerkat website.
Other SDKs: meerkat-python · meerkat-javascript
Table of contents
- About Meerkat
- What is Meerkat?
- What problem does it solve?
- Why use this gem?
- How it works
- Use cases
- Task types
- Self-host vs Meerkat Cloud
- Installation
- Quick start
- Examples
- Receiving webhooks (Rails)
- Output formats
- API reference
- Configuration
- Development
- License
What is Meerkat?
Meerkat gives developers a single primitive for agentic async work:
Register a task → Meerkat runs it → findings POST to your webhook
Describe a task in plain English, pass structured input_params, bring your own LLM key (BYOK), and Meerkat's agent executes the work — fetching webpages, extracting structured findings, and delivering JSON to your output_webhook on a schedule or on demand.
No polling. No per-carrier SDKs. No LLM tool loops to maintain.
This gem wraps the Meerkat REST API so you can create tasks, trigger runs, and verify inbound webhooks from Ruby and Rails apps without hand-rolling HTTP clients.
What problem does it solve?
Building async agent workflows usually means stitching together:
- A job queue and scheduler
- LLM calls with tool use (fetch URL, parse HTML, compare state)
- Webhook delivery and retry logic
- Change detection between runs
Meerkat handles all of that as a managed API. meerkat-agents handles the client side:
| Without the gem | With meerkat-agents |
|---|---|
Raw curl / Faraday calls |
Typed resource methods (client.tasks.create, client.tasks.run) |
| Manual auth headers | API key configured once |
| DIY webhook HMAC verification | Meerkat::Webhooks.verify + Rails concern |
| Error parsing by hand | Meerkat::NotFoundError, ValidationError, etc. |
Why use this gem?
- Webhook-native — every task delivers JSON to your endpoint; the gem helps you verify signatures
- BYOK — Anthropic, OpenAI, OpenRouter, or Grok; keys stored on your Meerkat account
- Recurring + one-off — natural-language schedules (
every 2 hours) or ad-hoc runs - Self-host or Cloud — same API against Docker, Render, Fly.io, or Meerkat Cloud
- Rails-ready — optional railtie, env-based config, webhook verification concern
How it works
┌─────────────┐ client.tasks.create ┌─────────────┐ agent + tools ┌─────────────┐
│ Your app │ ───────────────────────► │ Meerkat │ ──────────────────► │ Web / APIs │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ POST output_webhook │
└──────────────────────────────────────────┘
(verify with Meerkat::Webhooks)
| Step | What happens |
|---|---|
| 1. Register | client.tasks.create(...) — describe work, pass params, set webhook |
| 2. Run | Meerkat schedules recurring tasks or runs one-offs on demand |
| 3. Receive | Findings POSTed to your webhook as structured JSON |
Use cases
What teams ship with Meerkat — aligned with the use cases on meerkatagents.com:
| Use case | In one line | On the website |
|---|---|---|
| Package tracking | Webhook when DHL, UPS, FedEx, or any carrier status changes | Package tracking → |
| Website monitoring | Watch a URL; get notified when the signal you describe happens | Site monitoring → |
| Price & stock | Competitor price drops and restocks without building a scraper | Guide → |
| Async agent webhooks | Register LLM agent work; results POST to your endpoint | Agent webhooks → |
Read the full developer guides for step-by-step tutorials with API examples.
Package tracking — webhook when status changes
Monitor any courier tracking link — DHL, UPS, FedEx, USPS, DPD, Royal Mail — with one API. Meerkat's agent reads the carrier page on a schedule, detects state changes, and POSTs structured JSON to your endpoint. No per-carrier SDKs, no polling loops, no LLM tool code to maintain.
- Every carrier, one endpoint — pass a
courier_tracking_link; adding a new carrier is zero integration code. - Webhook on change only — hear from Meerkat when status moves (In transit → Out for delivery → Delivered), not on every poll.
- Signed and retried — HMAC-SHA256 signatures, exponential backoff, delivery history via the events API.
client.tasks.create(
task_type: "recurring",
description: "Monitor DHL tracking and report status changes",
input_params: { courier_tracking_link: "https://www.dhl.de/track?id=..." },
frequency: "every 2 hours",
output_webhook: "https://your-app.com/webhooks/meerkat"
)
→ Package tracking use case · Shipment tracking guide
Website monitoring — watch any URL
A change-detection API that understands intent, not just pixel diffs. Describe the signal in plain English — price under $99, plan name changed, status page updated — and Meerkat watches on a schedule. It only fires your webhook when that condition is met.
- Semantic, not cosmetic diff — no CSS selectors to maintain when the page layout shifts.
- JS-rendered pages — SPAs and dynamic content via headless fetch before reasoning.
- Your schedule — natural language (
every 15 minutes), cron, or on-demandclient.tasks.run(id).
client.tasks.create(
task_type: "recurring",
description: "Notify me when this product drops below $99",
input_params: { url: "https://shop.example.com/widget-pro" },
frequency: "every 30 minutes",
output_webhook: "https://your-app.com/webhooks/meerkat"
)
→ Website monitoring use case · Monitor website for changes guide
Price & stock monitoring — competitor alerts
Track competitor product pages and get a signed webhook when price drops, price increases, or stock flips (out of stock → in stock). One task per SKU — no scraper fleet, scheduler, or retry layer to operate.
client.tasks.create(
task_type: "recurring",
description: "Report price and stock whenever either changes",
input_params: { url: "https://competitor.com/product/sku-123" },
frequency: "every 15 minutes",
output_webhook: "https://your-app.com/webhooks/meerkat"
)
Example webhook when price moves:
{
"data": {
"summary": "Price dropped from 99.00 to 89.00",
"findings": { "price": 89.0, "previous_price": 99.0, "in_stock": true },
"change_detected": true
}
}
→ Competitor price & stock guide
Async agent webhooks — skip the scheduler boilerplate
Meerkat is the open source primitive for engineers who would rather ship product than rebuild schedulers, LLM tool loops, retries, and signed webhook delivery. Register a task in plain English, attach your LLM key (BYOK), point at an endpoint — done.
- One verb: register —
POST /taskscreates recurring or one-off agent work. - Webhook-native results — structured JSON, signed, retried until 2xx (same contract as Stripe or GitHub webhooks).
- MIT & self-hostable — Docker, Render, Fly.io, or Meerkat Cloud; identical REST API.
client.tasks.create(
task_type: "recurring",
description: "Summarize new arXiv papers in distributed systems",
input_params: { topic: "distributed systems" },
frequency: "0 9 * * *",
output_webhook: "https://your-app.com/webhooks/meerkat"
)
→ Agent webhooks use case · Webhook vs polling guide
One-off lookups
Ad-hoc agent tasks without standing up a schedule — research, single URL extraction, or on-demand runs triggered from your app:
client.tasks.create(
task_type: "one_off",
description: "Fetch the current price of this product once",
input_params: { url: "https://example.com/product/123" },
output_webhook: "https://your-app.com/webhooks/meerkat"
)
Scheduled scraping with webhooks
Need data on a cadence, not just change detection? Set a frequency and receive structured findings every run — see the web scraping API guide.
Task types
| Type | Behavior | Best for |
|---|---|---|
recurring |
Runs on a schedule; compares state between runs; reports changes | Courier tracking, uptime, price watches |
one_off |
Runs once, reports findings, marks task complete | Ad-hoc research, single lookups |
Frequency accepts natural language (every 30 minutes, hourly, every 2 hours) or cron expressions. Required for recurring tasks; omit for one_off.
Self-host vs Meerkat Cloud
| Self-host (OSS) | Meerkat Cloud | |
|---|---|---|
| API | Identical | Identical |
| LLM keys | You supply (BYOK) | You supply (BYOK) |
| Infra / ops | You run Postgres + workers | Managed for you |
| Base URL | http://localhost:3000/api/v1 |
https://cloud.meerkatagents.com/api/v1 (default) |
LLM usage is always billed by your provider. Meerkat never sees your model costs.
Self-host: meerkat README — Docker
Installation
Add to your Gemfile:
gem "meerkat-agents"
Or install directly:
gem install meerkat-agents
Then in your app:
require "meerkat"
Quick start
1. Get an API key
Sign up via the Meerkat Cloud dashboard or programmatically:
require "meerkat"
client = Meerkat::Client.new(base_url: "https://cloud.meerkatagents.com/api/v1")
result = client.signup.create(email: "you@company.com", name: "Your Name")
api_key = result.dig("data", "api_key") # store securely — shown once
2. Create a task
client = Meerkat::Client.new(api_key: ENV["MEERKAT_API_KEY"])
response = client.tasks.create(
task_type: "recurring",
description: "Monitor courier tracking and notify on status changes",
input_params: { courier_tracking_link: "https://..." },
frequency: "every 2 hours",
output_webhook: "https://your-app.com/webhooks/meerkat"
)
task = response["data"]
3. Trigger a run (optional)
client.tasks.run(task["id"], async: true)
Examples
List and filter tasks
client.tasks.list(status: "active", task_type: "recurring", limit: 20)
client.tasks.retrieve(1)
client.tasks.pause(1)
client.tasks.resume(1)
client.tasks.delete(1)
One-off lookup
client.tasks.create(
task_type: "one_off",
description: "Fetch the current price of this product",
input_params: { url: "https://example.com/product/123" },
output_webhook: "https://your-app.com/webhooks/meerkat"
)
Inspect runs and webhook delivery history
client.tasks.runs(1, limit: 10)
client.tasks.events(1, limit: 50)
API keys
client.api_keys.list
client.api_keys.create(name: "Production")
client.api_keys.revoke(key_id)
Receiving webhooks (Rails)
Every outbound POST is HMAC-SHA256 signed in the X-Meerkat-Signature header. Verify before processing:
Meerkat::Webhooks.verify(
payload: request.raw_post,
signature: request.headers["X-Meerkat-Signature"],
secret: ENV["MEERKAT_WEBHOOK_SECRET"]
)
Rails controller concern
class Webhooks::MeerkatController < ApplicationController
include Meerkat::Rails::WebhookVerification
def create
payload = JSON.parse(request.raw_post)
# handle event: run_completed, status_changed, etc.
head :ok
end
end
Set MEERKAT_WEBHOOK_SECRET in your environment.
Output formats
output_webhook is required on every task (or use "default" if your account has a default webhook configured). output_format is optional.
| Preset | Webhook payload shape |
|---|---|
default |
{ event, task_id, task_run_id, occurred_at, data: { summary, findings, change_detected, ... } } |
compact |
Top-level summary, change_detected, findings |
flat |
Findings merged into the webhook root object |
findings_only |
Findings object only |
minimal |
event, task_id, summary, change_detected |
client.tasks.create(
description: "...",
input_params: { url: "https://..." },
output_webhook: "https://your-app.com/hook",
output_format: "flat"
)
Any other string is passed to the agent as a custom instruction for shaping findings (max 500 chars).
API reference
Default base URL: https://cloud.meerkatagents.com/api/v1
All endpoints except signup require Authorization: Bearer mk_....
| SDK method | HTTP | Description |
|---|---|---|
client.signup.create(...) |
POST /signup |
Create account + API key |
client.api_keys.list |
GET /api_keys |
List API keys |
client.api_keys.create(name:) |
POST /api_keys |
Generate a new key |
client.api_keys.revoke(id) |
DELETE /api_keys/:id |
Revoke a key |
client.tasks.list(...) |
GET /tasks |
List tasks |
client.tasks.create(...) |
POST /tasks |
Create a task |
client.tasks.retrieve(id) |
GET /tasks/:id |
Task details + last known state |
client.tasks.update(id, ...) |
PATCH /tasks/:id |
Partial update |
client.tasks.replace(id, ...) |
PUT /tasks/:id |
Full replace |
client.tasks.delete(id) |
DELETE /tasks/:id |
Archive or delete |
client.tasks.run(id, async:) |
POST /tasks/:id/run |
Trigger on-demand run |
client.tasks.pause(id) |
POST /tasks/:id/pause |
Pause recurring task |
client.tasks.resume(id) |
POST /tasks/:id/resume |
Resume recurring task |
client.tasks.runs(id) |
GET /tasks/:id/runs |
Run history |
client.tasks.events(id) |
GET /tasks/:id/events |
Webhook delivery log |
Full OpenAPI spec: meerkat/openapi/openapi.yaml
Configuration
Environment variables
| Variable | Description |
|---|---|
MEERKAT_API_KEY |
API key from signup (mk_...) |
MEERKAT_BASE_URL |
API base URL (default: Meerkat Cloud) |
MEERKAT_WEBHOOK_SECRET |
Secret for verifying inbound webhooks |
Rails / global config
Meerkat.configure do |config|
config.api_key = ENV["MEERKAT_API_KEY"]
config.base_url = ENV.fetch("MEERKAT_BASE_URL", Meerkat::Client::DEFAULT_BASE_URL)
end
Meerkat.configuration.client.tasks.list(status: "active")
Self-hosted instance
client = Meerkat::Client.new(
api_key: ENV["MEERKAT_API_KEY"],
base_url: "http://localhost:3000/api/v1"
)
Development
bundle install
bundle exec rake spec
See PUBLISHING.md for local testing and RubyGems release steps.
Related projects
- meerkat — open source API server
- meerkat-python — Python SDK
- meerkat-javascript — JavaScript/TypeScript SDK
License
MIT — see LICENSE.