AlterLabAlterLab
PricingComparePlaygroundBlogDocsChangelog
    AlterLabAlterLab
    PricingComparePlaygroundBlogDocsChangelog
    IntroductionQuickstartInstallationYour First Request
    REST APIJob PollingAPI KeysSessions APINew
    OverviewPythonNode.js
    JavaScript RenderingOutput FormatsPDF & OCRCachingWebhooksJSON Schema FilteringWebSocket Real-TimeBring Your Own ProxyProAuthenticated ScrapingNewWeb CrawlingBatch ScrapingSchedulerChange DetectionCloud Storage ExportSpend LimitsOrganizations & TeamsAlerts & Notifications
    Structured ExtractionAIE-commerce ScrapingNews MonitoringPrice MonitoringMulti-Page CrawlingMonitoring DashboardAI Agent / MCPMCPData Pipeline to Cloud
    PricingRate LimitsError Codes
    From FirecrawlFrom ApifyFrom ScrapingBee / ScraperAPI
    PlaygroundPricingStatus
    Guide

    Webhooks

    Receive real-time notifications when your scraping jobs complete, fail, or require attention. No more polling - let AlterLab push results directly to your server.

    Manage your webhook endpoints, test deliveries, and review delivery logs from the Webhooks dashboard.

    Why Use Webhooks?

    Real-Time Notifications

    Get instant notifications when jobs complete instead of constantly polling the API.

    Reduced API Calls

    Save on rate limits and API usage by receiving push notifications instead of polling.

    Event-Driven Architecture

    Build reactive systems that respond immediately to scraping events.

    Automatic Retries

    AlterLab automatically retries failed deliveries with exponential backoff.

    Webhooks vs WebSocket

    Webhooks are best for server-to-server communication and batch processing.WebSocket is better for real-time dashboards and client-side updates. You can use both simultaneously.

    How Webhooks Work

    1. Job Completes

    When a scraping job finishes (success or failure), AlterLab checks if you have webhooks configured for that event type.

    2. Payload Signed

    The event payload is JSON-serialized and signed with your webhook secret using HMAC-SHA256. The signature is included in the X-Alterlab-Signature header.

    3. HTTP POST Sent

    AlterLab sends an HTTP POST request to your webhook URL with the signed payload. Your server must respond within 10 seconds.

    4. Response Handled

    A 2xx response indicates success. Any other status code or timeout triggers automatic retries (up to 3 attempts).

    Setup Guide

    Step 1: Create a Webhook Endpoint

    Create an HTTP endpoint on your server that accepts POST requests:

    Python
    from flask import Flask, request, jsonify
    import hmac
    import hashlib
    
    app = Flask(__name__)
    WEBHOOK_SECRET = "whsec_..."  # Your webhook secret
    
    @app.route("/webhooks/alterlab", methods=["POST"])
    def handle_alterlab_webhook():
        # Verify signature
        signature = request.headers.get("X-Alterlab-Signature", "")
        expected_sig = "sha256=" + hmac.new(
            WEBHOOK_SECRET.encode(),
            request.data,
            hashlib.sha256
        ).hexdigest()
    
        if not hmac.compare_digest(signature, expected_sig):
            return jsonify({"error": "Invalid signature"}), 401
    
        # Process the event
        event = request.json
        event_type = event.get("event")
    
        if event_type == "job.completed":
            job_data = event.get("data", {})
            print(f"Job completed: {job_data.get('job_id')}")
            # Process the scraped content...
    
        elif event_type == "job.failed":
            print(f"Job failed: {event.get('data', {}).get('error')}")
    
        return jsonify({"received": True}), 200

    Step 2: Register the Webhook

    Register your endpoint with AlterLab via the dashboard or API:

    Bash
    curl -X POST https://api.alterlab.io/api/v1/user-webhooks \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Production Webhook",
        "url": "https://your-server.com/webhooks/alterlab",
        "events": ["job.completed", "job.failed"]
      }'

    Save Your Secret!

    The webhook secret is only returned once during creation. Save it securely - you'll need it to verify webhook signatures.

    Step 3: Test the Webhook

    Send a test event to verify your endpoint is working:

    Bash
    curl -X POST https://api.alterlab.io/api/v1/user-webhooks/{webhook_id}/test \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"event_type": "job.completed"}'

    Event Types

    EventDescriptionWhen Triggered
    job.completedScraping job finished successfullyJob status changes to "succeeded"
    job.failedScraping job failed after all retriesJob status changes to "failed"
    job.startedScraping job began processingWorker picks up the job
    batch.completedAll jobs in a batch finishedLast job in batch completes
    credits.lowBalance below thresholdBalance drops below 10% of plan
    credits.exhaustedBalance depletedBalance reaches zero

    Payload Format

    All webhook payloads follow a consistent JSON structure:

    job.completed Payload

    JSON
    {
      "event": "job.completed",
      "timestamp": "2025-01-15T10:30:45.123Z",
      "data": {
        "job_id": "550e8400-e29b-41d4-a716-446655440000",
        "batch_id": null,
        "url": "https://example.com/page",
        "status": "succeeded",
        "result": {
          "status_code": 200,
          "content": "<!DOCTYPE html>...",
          "title": "Example Page",
          "metadata": {
            "description": "Page description",
            "keywords": ["example"]
          }
        },
        "billing": {
          "total_credits": 3,
          "tier_used": "3",
          "escalations": [
            {"tier": "1", "result": "failed", "credits": 1},
            {"tier": "3", "result": "success", "credits": 3}
          ]
        },
        "timing": {
          "queued_at": "2025-01-15T10:30:30.000Z",
          "started_at": "2025-01-15T10:30:32.000Z",
          "completed_at": "2025-01-15T10:30:45.123Z",
          "duration_ms": 13123
        }
      }
    }

    job.failed Payload

    JSON
    {
      "event": "job.failed",
      "timestamp": "2025-01-15T10:31:00.000Z",
      "data": {
        "job_id": "550e8400-e29b-41d4-a716-446655440001",
        "url": "https://protected-site.com",
        "status": "failed",
        "error": {
          "code": "BLOCKED_BY_ANTIBOT",
          "message": "Site blocked access after all tier escalations",
          "details": {
            "last_tier": "4",
            "attempts": 6
          }
        },
        "billing": {
          "total_credits": 0,
          "refunded": true,
          "reason": "All tiers failed"
        }
      }
    }

    HTTP Headers

    HeaderDescriptionExample
    X-Alterlab-SignatureHMAC-SHA256 signature of payloadsha256=abc123...
    X-Alterlab-EventEvent typejob.completed
    X-Alterlab-DeliveryDelivery ID for trackingdel_550e8400...
    Content-TypeAlways JSONapplication/json
    User-AgentAlterLab identifierAlterLab-Webhook/1.0

    Signature Verification

    Always Verify Signatures

    Never process webhook payloads without verifying the signature first. This prevents attackers from sending fake events to your endpoint.

    Verification Steps

    1. Get the raw request body (before JSON parsing)
    2. Get the X-Alterlab-Signature header
    3. Compute HMAC-SHA256 of the body using your webhook secret
    4. Compare the computed signature with the header value
    5. Use constant-time comparison to prevent timing attacks
    Python
    import hmac
    import hashlib
    
    def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
        """Verify the webhook signature using HMAC-SHA256."""
        expected = "sha256=" + hmac.new(
            secret.encode("utf-8"),
            payload,
            hashlib.sha256
        ).hexdigest()
    
        # Use constant-time comparison
        return hmac.compare_digest(signature, expected)
    
    # Usage in Flask
    @app.route("/webhooks/alterlab", methods=["POST"])
    def handle_webhook():
        signature = request.headers.get("X-Alterlab-Signature", "")
    
        if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
            return jsonify({"error": "Invalid signature"}), 401
    
        # Safe to process...

    API Reference

    Create Webhook

    POST
    /api/v1/user-webhooks

    Create a new webhook endpoint. The signing secret is only returned once.

    Parameters

    NameTypeRequiredDescription
    namestring
    Required
    Display name for the webhook
    urlstring
    Required
    HTTPS URL to receive webhook events
    eventsstring[]
    Required
    Event types to subscribe to

    Request Example

    Bash
    curl -X POST https://api.alterlab.io/api/v1/user-webhooks \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Production Webhook",
        "url": "https://your-server.com/webhooks/alterlab",
        "events": ["job.completed", "job.failed"]
      }'

    Response Example

    JSON
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Production Webhook",
      "url": "https://your-server.com/webhooks/alterlab",
      "events": ["job.completed", "job.failed"],
      "is_active": true,
      "failure_count": 0,
      "secret": "whsec_abc123...",  // Only returned once!
      "created_at": "2025-01-15T10:30:00Z"
    }

    List Webhooks

    GET
    /api/v1/user-webhooks

    List all webhooks for the authenticated user.

    Parameters

    NameTypeRequiredDescription
    include_inactivebooleanOptionalInclude disabled webhooksDefault: false

    Request Example

    Bash
    curl -X GET https://api.alterlab.io/api/v1/user-webhooks \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN"

    Response Example

    JSON
    {
      "webhooks": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Production Webhook",
          "url": "https://your-server.com/webhooks/alterlab",
          "events": ["job.completed", "job.failed"],
          "is_active": true,
          "failure_count": 0,
          "last_success_at": "2025-01-15T10:30:45Z"
        }
      ],
      "total": 1
    }

    Test Webhook

    POST
    /api/v1/user-webhooks/{webhook_id}/test

    Send a test event to verify your webhook endpoint is working correctly.

    Parameters

    NameTypeRequiredDescription
    webhook_iduuid
    Required
    Webhook ID to test
    event_typestringOptionalEvent type to simulateDefault: job.completed

    Request Example

    Bash
    curl -X POST https://api.alterlab.io/api/v1/user-webhooks/550e8400.../test \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"event_type": "job.completed"}'

    Response Example

    JSON
    {
      "success": true,
      "status_code": 200,
      "response_time_ms": 234,
      "error": null
    }

    Rotate Secret

    POST
    /api/v1/user-webhooks/{webhook_id}/rotate-secret

    Generate a new signing secret. Update your server immediately after rotating.

    Parameters

    NameTypeRequiredDescription
    webhook_iduuid
    Required
    Webhook ID

    Request Example

    Bash
    curl -X POST https://api.alterlab.io/api/v1/user-webhooks/550e8400.../rotate-secret \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN"

    Response Example

    JSON
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "secret": "whsec_newSecret123...",  // New secret
      ...
    }

    View Delivery History

    GET
    /api/v1/user-webhooks/{webhook_id}/deliveries

    View delivery history and debug failed deliveries.

    Parameters

    NameTypeRequiredDescription
    webhook_iduuid
    Required
    Webhook ID
    limitintegerOptionalNumber of deliveries to returnDefault: 50
    statusstringOptionalFilter by status: pending, success, failed, retrying

    Request Example

    Bash
    curl -X GET "https://api.alterlab.io/api/v1/user-webhooks/550e8400.../deliveries?limit=10" \
      -H "Authorization: Bearer YOUR_SESSION_TOKEN"

    Response Example

    JSON
    {
      "deliveries": [
        {
          "id": "del_123...",
          "event_type": "job.completed",
          "status": "success",
          "attempt_count": 1,
          "max_attempts": 3,
          "response_status_code": 200,
          "response_time_ms": 234,
          "error_message": null,
          "created_at": "2025-01-15T10:30:45Z"
        },
        {
          "id": "del_456...",
          "event_type": "job.completed",
          "status": "failed",
          "attempt_count": 3,
          "max_attempts": 3,
          "response_status_code": 500,
          "response_time_ms": 1200,
          "error_message": "Server returned 500",
          "created_at": "2025-01-15T09:15:00Z"
        }
      ],
      "total": 2
    }

    Retry Behavior

    AttemptDelayTotal Wait
    1 (Initial)Immediate0s
    2 (First retry)1 minute1m
    3 (Final retry)5 minutes6m

    Automatic Disabling

    After 5 consecutive failures across multiple events, the webhook is automatically disabled to prevent wasting resources. Re-enable it in the dashboard after fixing the issue.

    Best Practices

    1. Respond Quickly

    Return a 200 response as soon as possible (within 10 seconds). Process the payload asynchronously if needed. A slow response triggers unnecessary retries.

    Python
    @app.route("/webhooks/alterlab", methods=["POST"])
    def handle_webhook():
        # Verify signature first
        if not verify_signature(request):
            return "", 401
    
        # Queue for async processing
        queue.enqueue(process_webhook, request.json)
    
        # Return immediately
        return "", 200

    2. Handle Duplicates

    Due to retries, you may receive the same event multiple times. Use thejob_id or delivery ID to deduplicate events.

    3. Use HTTPS

    Webhook URLs must use HTTPS. HTTP endpoints are not supported for security reasons.

    4. Rotate Secrets Periodically

    Rotate your webhook secret every few months. Use the rotate-secret endpoint and update your server configuration immediately.

    5. Monitor Delivery Stats

    Check the webhook detail endpoint periodically to monitor success rates and catch issues early.

    Webhook Limits

    • Maximum webhooks per user: 10
    • Request timeout: 10 seconds
    • Maximum retries: 3 attempts
    • Auto-disable threshold: 5 consecutive failures
    • Payload size limit: 1MB
    CachingJSON Schema Filtering
    Last updated: March 2026

    On this page