Describe a task in plain English. Meerkat runs it async — results POST to your webhook.
Official Python client for Meerkat — an open source, webhook-native API for async agent tasks.
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 Python package (meerkat-python). For product overview, use cases, and self-host vs Cloud, read more on the Meerkat website.
Other SDKs: meerkat-ruby · meerkat-javascript
- About Meerkat
- What is Meerkat?
- What problem does it solve?
- Why use this package?
- How it works
- Use cases
- Task types
- Self-host vs Meerkat Cloud
- Installation
- Quick start
- Examples
- Receiving webhooks
- Output formats
- API reference
- Configuration
- Development
- License
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 package wraps the Meerkat REST API so you can create tasks, trigger runs, and verify inbound webhooks from Python apps, FastAPI services, Django backends, and scripts.
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-python handles the client side:
| Without the package | With meerkat-python |
|---|---|
Raw httpx / requests calls |
Resource methods (client.tasks.create, client.tasks.run) |
| Manual auth headers | API key configured once |
| DIY webhook HMAC verification | meerkat.verify_webhook() |
| Error parsing by hand | NotFoundError, ValidationError, etc. |
- Webhook-native — every task delivers JSON to your endpoint; verify signatures with one helper
- 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
- Framework-friendly — works with FastAPI, Django, Flask, Celery, and plain scripts
┌─────────────┐ client.tasks.create ┌─────────────┐ agent + tools ┌─────────────┐
│ Your app │ ───────────────────────► │ Meerkat │ ──────────────────► │ Web / APIs │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ POST output_webhook │
└──────────────────────────────────────────┘
(verify with meerkat.verify_webhook)
| 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 |
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.
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
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(task_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
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
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
Ad-hoc agent tasks without standing up a schedule — research, single URL extraction, or on-demand runs triggered from your API:
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",
)Need data on a cadence, not just change detection? Set a frequency and receive structured findings every run — see the web scraping API guide.
| 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 (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
pip install meerkat-pythonSign up via the Meerkat Cloud dashboard or programmatically:
from meerkat import Meerkat
with Meerkat(base_url="https://cloud.meerkatagents.com/api/v1") as client:
result = client.signup.create(email="you@company.com", name="Your Name")
api_key = result["data"]["api_key"] # store securely — shown oncefrom meerkat import Meerkat
with Meerkat(api_key="mk_...") as client:
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"] client.tasks.run(task["id"], async_=True)client.tasks.list(status="active", task_type="recurring", limit=20)
client.tasks.retrieve(1)
client.tasks.update(1, description="Updated description")
client.tasks.pause(1)
client.tasks.resume(1)
client.tasks.delete(1)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",
)client.tasks.runs(1, limit=10)
client.tasks.events(1, limit=50)client.api_keys.list()
client.api_keys.create(name="Production")
client.api_keys.revoke(key_id)Every outbound POST is HMAC-SHA256 signed in the X-Meerkat-Signature header.
from meerkat import verify_webhook
verify_webhook(
payload=raw_body,
signature=headers.get("X-Meerkat-Signature"),
secret=os.environ["MEERKAT_WEBHOOK_SECRET"],
)import os
from fastapi import FastAPI, Header, HTTPException, Request
from meerkat import verify_webhook
app = FastAPI()
@app.post("/webhooks/meerkat")
async def meerkat_webhook(
request: Request,
x_meerkat_signature: str | None = Header(default=None),
):
body = await request.body()
if not verify_webhook(
payload=body,
signature=x_meerkat_signature,
secret=os.environ["MEERKAT_WEBHOOK_SECRET"],
):
raise HTTPException(status_code=401)
payload = await request.json()
return {"ok": True}from meerkat import verify_webhook
def meerkat_webhook(request):
if not verify_webhook(
payload=request.body,
signature=request.headers.get("X-Meerkat-Signature"),
secret=settings.MEERKAT_WEBHOOK_SECRET,
):
return HttpResponse(status=401)
...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).
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
| 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 |
import os
from meerkat import Meerkat
client = Meerkat(
api_key=os.environ["MEERKAT_API_KEY"],
base_url="http://localhost:3000/api/v1",
)python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest
python -m buildSee PUBLISHING.md for PyPI release steps.
- meerkat — open source API server
- meerkat-ruby — Ruby SDK
- meerkat-javascript — JavaScript/TypeScript SDK
MIT — see LICENSE.