Webhooks

Webhook Delivery

Receive HMAC-SHA256 signed HTTP POST callbacks for each detected tweet. Webhooks provide reliable delivery with automatic retries.

Method:POST
Signing:HMAC-SHA256
Timeout:10 seconds
Retries:3 attempts

Request Headers

HeaderDescription
X-ScrapeBadger-SignatureHMAC-SHA256 signature: sha256=hex_digest
X-ScrapeBadger-Delivery-IdUnique delivery ID for idempotency
X-ScrapeBadger-EventEvent type: tweet or test
Content-Typeapplication/json
User-AgentScrapeBadger-Webhook/1.0

Webhook Payload

// Webhook POST body (JSON)
{
  "type": "tweet",
  "monitor_id": "mon-123",
  "tweet_id": "1234567890123456789",
  "author_username": "elonmusk",
  "tweet_published_at": "2026-03-04T12:00:00Z",
  "detected_at": "2026-03-04T12:00:00.850Z",
  "latency_ms": 850,
  "tweet": {
    "id": "1234567890123456789",
    "text": "The future is electric...",
    "created_at": "Mon Mar 04 12:00:00 +0000 2026",
    "user_id": "44196397",
    "username": "elonmusk",
    "user_name": "Elon Musk",
    "favorite_count": 0,
    "retweet_count": 0,
    "reply_count": 0,
    "media": [],
    "urls": [],
    "hashtags": []
  }
}

Signature Verification

Always verify the X-ScrapeBadger-Signature header to ensure the request is authentic. The signature is computed as HMAC-SHA256(webhook_secret, request_body).

Node.js / Express

import crypto from 'crypto';

function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  const received = signature.replace('sha256=', '');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}

// Express.js example
app.post('/webhooks/tweets', (req, res) => {
  const signature = req.headers['x-scrapebadger-signature'];
  const body = JSON.stringify(req.body);

  if (!verifyWebhookSignature(body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const tweet = req.body;
  console.log('New tweet from', tweet.author_username);
  res.status(200).send('OK');
});

Python / Flask

import hmac
import hashlib
from flask import Flask, request

app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    received = signature.replace("sha256=", "")
    return hmac.compare_digest(expected, received)

@app.post("/webhooks/tweets")
def handle_webhook():
    signature = request.headers.get("X-ScrapeBadger-Signature", "")
    if not verify_signature(request.data, signature, WEBHOOK_SECRET):
        return "Invalid signature", 401

    data = request.json
    print(f"New tweet from @{data['author_username']}")
    return "OK", 200

Response Requirements

Success Response

Return any 2xx status code within 10 seconds to acknowledge delivery. The response body is ignored.

Retry Logic

Failed deliveries (non-2xx or timeout) are retried up to 3 times with exponential backoff. After all retries are exhausted, the delivery is marked as webhook_failed in the delivery log.

Idempotency

Use the X-ScrapeBadger-Delivery-Id header to deduplicate webhook deliveries on your end in case of retries.

Testing

Use the POST /v1/twitter/stream/webhooks/test endpoint to send a test payload to your webhook URL and verify it is receiving and processing events correctly.