Real-time rate alerts delivered to your endpoint. Set conditions, get notified when rates hit your thresholds - with full decision context.
Real-Time Alerts
Stop polling. Start reacting instantly when rates change.
Get alerts within seconds of a rate change, not hours later when your cron job runs. Sub-second delivery to your endpoint.
Eliminate the infrastructure cost and complexity of constantly checking for rate changes. We monitor, you react.
Every webhook includes the complete decision snapshot. Act immediately without making follow-up API calls.
Trigger only when it matters: APR below a threshold, rate drops by a percentage, or payment under a target amount.
Verify every webhook is authentic with HMAC-SHA256 signatures. Event IDs prevent replay attacks.
Simulate webhook deliveries to verify your handler works correctly. Ship with confidence.
The Flow
From monitor creation to webhook delivery in four steps
Set your conditions: state, product type, thresholds
RateAPI continuously monitors rate changes
Your threshold is triggered by a rate change
Full decision context delivered to your endpoint
// Your endpoint receives a POST request
// Headers include signature for verification
POST https://your-app.com/webhooks/rateapi
X-RateAPI-Signature: sha256=abc123...
X-RateAPI-Timestamp: 1706234567
X-RateAPI-Event-Id: evt_xyz789
{
"event": "decision.monitor.triggered",
"trigger_type": "condition_met",
"why_now": ["APR dropped below 6.25% threshold"],
"decision_snapshot": { /* full /v1/decisions response */ }
}
Quick Start
Set up your first webhook monitor in minutes
Generate an API key to authenticate your requests. Store it securely - you'll need it for all API calls and it can't be retrieved again.
curl -X POST https://api.rateapi.dev/keys
# Response
{
"key": "rk_live_abc123...",
"id": "key_xyz789"
}
Create an HTTPS endpoint on your server to receive webhook payloads. The endpoint must respond with a 2xx status code within 5 seconds.
const express = require('express');
const app = express();
app.post('/webhooks/rateapi', express.json(), (req, res) => {
const { event, trigger_type, why_now, decision_snapshot } = req.body;
// Verify signature (see Security section)
// Process the rate alert
console.log(`Rate alert: ${why_now[0]}`);
// Respond quickly to acknowledge receipt
res.status(200).json({ received: true });
});
Define the conditions that should trigger a webhook. You can monitor specific states, product types, and set threshold conditions.
curl -X POST https://api.rateapi.dev/v1/monitors \
-H "Authorization: Bearer rk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"name": "CA Low APR Alert",
"webhook_url": "https://your-app.com/webhooks/rateapi",
"state": "CA",
"product_type": "30yr_fixed",
"loan_amount": 500000,
"conditions": [
{
"field": "apr",
"operator": "lt",
"value": 6.25
}
],
"cooldown_minutes": 60
}'
# Response
{
"id": "mon_abc123",
"status": "active",
"webhook_secret": "whsec_xyz789..."
}
The webhook_secret is returned only once when you create the monitor. Store it securely - you'll need it to verify webhook signatures.
Before waiting for real rate changes, test your webhook handler using the simulate endpoint. This sends a test payload to your webhook URL.
curl -X POST https://api.rateapi.dev/v1/monitors/mon_abc123/simulate \
-H "Authorization: Bearer rk_live_abc123..."
# Response
{
"success": true,
"delivered": true,
"response_code": 200,
"response_time_ms": 124
}
The simulated webhook will include "test": true in the payload so your handler can distinguish test events from real alerts.
Reference
Complete documentation of the webhook payload structure
{
"event": "decision.monitor.triggered",
"event_id": "evt_abc123xyz789",
"monitor_id": "mon_abc123",
"customer_id": "key_xyz789",
"evaluated_at": "2024-01-25T14:30:00Z",
"trigger_type": "condition_met",
"why_now": [
"APR dropped below 6.25% threshold (now 6.125%)",
"Navy Federal offering $127/mo savings vs current rate"
],
"rate_change": {
"state": "CA",
"product_type": "30yr_fixed",
"previous_rate": 6.375,
"new_rate": 6.125,
"rate_delta": -0.25
},
"condition_results": [
{
"field": "apr",
"operator": "lt",
"threshold": 6.25,
"actual_value": 6.125,
"passed": true
}
],
"decision_snapshot": {
"summary": {
"recommended_action": "shop_providers",
"confidence": "high"
},
"actions": [
{
"type": "compare_offers",
"offers": [...]
}
]
},
"test": false
}
| Field | Type | Description |
|---|---|---|
event |
string | decision.monitor.triggered for real alerts, decision.monitor.test for simulations |
event_id |
string | Unique identifier for this event. Use for deduplication and idempotency. |
monitor_id |
string | The ID of the monitor that triggered this webhook |
customer_id |
string | Your API key ID (not the key itself) |
evaluated_at |
ISO 8601 | Timestamp when the conditions were evaluated |
trigger_type |
string | One of: condition_met, recommendation_changed, threshold_crossed, new_offer_detected |
why_now |
string[] | Human-readable explanations of why the webhook was triggered |
test |
boolean | true if this is a simulated webhook, false for real alerts |
| Field | Type | Description |
|---|---|---|
state |
string | Two-letter state code (e.g., "CA", "TX") |
product_type |
string | Mortgage product type (e.g., "30yr_fixed", "15yr_fixed") |
previous_rate |
number | The rate before the change (percentage) |
new_rate |
number | The current rate after the change (percentage) |
rate_delta |
number | The change in rate (negative = decrease) |
| Field | Type | Description |
|---|---|---|
field |
string | The field that was evaluated (rate, apr, monthly_payment) |
operator |
string | The comparison operator used |
threshold |
number | The threshold value from your condition |
actual_value |
number | The actual value at evaluation time |
passed |
boolean | Whether this condition passed |
Contains the full response from the /v1/decisions endpoint at the time of evaluation. This includes recommended actions, top offers, savings calculations, and provider details. See the Decisions API documentation for the complete schema.
Security
Verify webhook authenticity using HMAC-SHA256 signatures
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const actual = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(actual)
);
}
// In your webhook handler:
app.post('/webhooks/rateapi', (req, res) => {
const signature = req.headers['x-rateapi-signature'];
const timestamp = req.headers['x-rateapi-timestamp'];
// Reject old webhooks (> 5 minutes)
if (Date.now() / 1000 - timestamp > 300) {
return res.status(401).send('Expired');
}
if (!verifySignature(req.rawBody, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook...
});
import hmac
import hashlib
import time
def verify_signature(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
actual = signature.replace('sha256=', '')
return hmac.compare_digest(expected, actual)
# In your webhook handler:
from flask import Flask, request
@app.route('/webhooks/rateapi', methods=['POST'])
def webhook_handler():
signature = request.headers.get('X-RateAPI-Signature')
timestamp = request.headers.get('X-RateAPI-Timestamp')
# Reject old webhooks (> 5 minutes)
if time.time() - int(timestamp) > 300:
return 'Expired', 401
if not verify_signature(
request.data.decode(),
signature,
WEBHOOK_SECRET
):
return 'Invalid signature', 401
# Process webhook...
Configuration
Define when your webhooks should fire
The metric to monitor
rate
apr
monthly_payment
How to compare against your threshold
lt
lte
gt
gte
eq
change_pct
// Alert when APR drops below 6.25%
{
"field": "apr",
"operator": "lt",
"value": 6.25
}
// Alert when rate drops by more than 0.125% (12.5 basis points)
{
"field": "rate",
"operator": "change_pct",
"value": -2.0 // -2% change in rate value
}
// Alert when monthly payment falls below $2,500
{
"field": "monthly_payment",
"operator": "lt",
"value": 2500
}
// Combine multiple conditions (all must pass)
"conditions": [
{ "field": "apr", "operator": "lt", "value": 6.5 },
{ "field": "monthly_payment", "operator": "lt", "value": 3000 }
]
Endpoints
Complete CRUD operations for monitor management
Production Tips
Recommendations for reliable webhook handling
Set a cooldown (e.g., 60 minutes) to prevent alert fatigue from minor rate fluctuations. You'll only receive one webhook per cooldown period.
Store the event_id from each webhook and check for duplicates before processing. This handles retries safely.
Acknowledge webhooks within 5 seconds by returning a 2xx status. Queue heavy processing for async execution.
If your endpoint fails, we'll retry with exponential backoff. Design your handler to be idempotent and log failed attempts.
Check the HMAC-SHA256 signature on every webhook before processing. Never trust unverified payloads.
Log the full payload, headers, and your processing result. This makes debugging much easier when something goes wrong.
Create your first monitor and start receiving real-time rate alerts