Recall First Request access

For AI agents

Let your AI assistant or your own software watch FDA recalls for you. Three ways in, all with the same API key: an MCP server, a REST API and webhooks.

Access is by request for now. Once you're approved, make a key on your account page under For AI agents and your own software. Each key belongs to your account and can be removed at any time.

1. MCP server

The address is https://recallfirst.com/mcp and every request sends Authorization: Bearer rf_….

Claude Code

claude mcp add --transport http recall-first https://recallfirst.com/mcp \
  --header "Authorization: Bearer rf_YOUR_KEY"

Cursor, Claude Desktop and other apps that take a JSON config

{
  "mcpServers": {
    "recall-first": {
      "url": "https://recallfirst.com/mcp",
      "headers": { "Authorization": "Bearer rf_YOUR_KEY" }
    }
  }
}

Apps that only accept sign-in by OAuth, such as custom connectors in Claude.ai or ChatGPT, can't use a key yet. Use the REST API or webhooks with those for now.

ToolWhat it does
add_watchWatch words, firms, categories, a class and states. Returns how many recalls from the last 90 days already match.
list_watchesYour watches.
remove_watchStop a watch.
list_matchesRecalls that matched, newest first, with why. Pass since to get only new ones.
get_recallOne recall's full record and its FDA link.
search_recallsSearch recent recalls by word, category and class.

Try asking: "Watch for any recall of kratom or tianeptine sold in New Jersey, and tell me what's matched this month."

2. REST API

KEY=rf_YOUR_KEY
# Add a watch
curl -s https://recallfirst.com/v1/watches -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{"name":"Supplements","terms":["kratom","ashwagandha"],"categories":["food"],"min_class":2,"states":["NJ","NY"]}'
# What matched since yesterday
curl -s "https://recallfirst.com/v1/matches?since=2026-10-21T00:00:00Z" -H "Authorization: Bearer $KEY"
# One recall
curl -s https://recallfirst.com/v1/recalls/F-0123-2027 -H "Authorization: Bearer $KEY"
CallWhat it does
GET /v1/watchesYour watches.
POST /v1/watchesAdd a watch: name, terms, firms, categories (food, drug, device), min_class (1 to 3), states.
PUT /v1/watches/{id}Change a watch.
DELETE /v1/watches/{id}Remove a watch.
GET /v1/matchesMatches, newest first. Optional: since (ISO time), watch_id, limit (up to 200).
GET /v1/recallsSearch: q, category, class, limit.
GET /v1/recalls/{id}One recall.

3. Webhooks

Save an https address on your account page. Each hour that something new matches, we POST it there once:

{
  "event": "recall.matched",
  "sent_at": "2026-10-22T14:23:05.000Z",
  "matches": [{
    "watch": { "id": 12, "name": "Supplements" },
    "why": ["mentions \"kratom\""],
    "recall": { "id": "F-0123-2027", "classification": "Class I", "firm": "…", "product": "…",
                "reason": "…", "distribution": "Nationwide", "date": "2026-10-21", "url": "https://…" }
  }]
}

Check that it came from us: the X-RecallFirst-Signature header is sha256= followed by the HMAC-SHA256 of the raw body, keyed with the secret shown when you saved the address.

import hmac, hashlib
def from_recall_first(raw_body: bytes, header: str, secret: str) -> bool:
    want = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, header)

We try each delivery once. If your server was down, read what you missed with GET /v1/matches?since=….

For crawlers and agents

A plain summary of this page is at /llms.txt.