SILENT GOD ENTERPRISE | ResilientLink is a web scraping API by Silent God Enterprise — extract clean, structured data from any URL in a single request.
ResilientLink — Developer Reference

Webhook
Integration Guide.

Receive real-time HTTP callbacks the moment a scrape event completes — no polling, no delays. This guide walks you through setup, verification, event handling, and production best practices.

● Live POST — application/json Requires: Active Plan
01 — Overview

How webhooks work.

When you make an API scrape request, ResilientLink processes it and — once complete — fires an HTTP POST to the endpoint URL you have configured in your dashboard. Your server receives the event payload, processes it, and returns 200 OK.

This is entirely asynchronous. You do not wait for the webhook inside your original API call — the scrape response comes back immediately, and the webhook fires in the background once the job settles.

Webhooks are optional. If you have not configured an endpoint, or your webhook status is Inactive, ResilientLink skips delivery silently. Your scrape results are still returned normally via the API response.
02 — Dashboard Setup

Configure your endpoint.

All webhook settings live in your ResilientLink dashboard under Webhooks. No code deployment needed to change them.

Your endpoint must be reachable from the public internet. Servers behind a firewall, VPN, or running on localhost will not receive deliveries. Use a tunnel like ngrok during local development.
03 — Webhook Events

What triggers a webhook.

ResilientLink fires webhooks for three event types. Every payload shares the same outer envelope — only the data object differs.

Event When it fires Frequency
scrape.completed Metadata extraction succeeded and structured data was returned. Every successful scrape
scrape.failed The scrape request errored, timed out, or the target returned an error status. Every failed scrape
quota.warning Your monthly request usage crossed 80% or 95% of your effective limit. Once per threshold per calendar month
04 — Payload Reference

What your server receives.

All webhook requests are HTTP POST with Content-Type: application/json. The body always follows this structure:

scrape.completed — full payload JSON
{
  "event":     "scrape.completed",
  "timestamp": "2026-05-19T12:00:00.000Z",
  "data": {
    "url":           "https://example.com/article",
    "status":        "success",
    "status_code":   200,
    "response_time": 312,          // milliseconds
    "metadata": {
      "title":      "Example Domain",
      "description":"This domain is for illustrative examples.",
      "image":      "https://example.com/og.png",
      "domain":     "example.com",
      "word_count": 142,
      "scraped_at": "2026-05-19T12:00:00.000Z"
    }
  }
}
scrape.failed — full payload JSON
{
  "event":     "scrape.failed",
  "timestamp": "2026-05-19T12:01:00.000Z",
  "data": {
    "url":        "https://example.com/blocked-page",
    "status":     "failed",
    "status_code":403,             // HTTP status from target, null if network error
    "error":      "Target returned 403 Forbidden",
    "metadata":   {}
  }
}
status_code in a scrape.failed payload is the HTTP status returned by the target website, not by ResilientLink. It may be null if the failure was a network-level error (timeout, DNS failure, etc.).
Field Reference — top-level envelope
FieldTypeDescription
eventstringEvent type identifier. One of scrape.completed, scrape.failed, quota.warning.
timestampstring (ISO 8601)UTC time the event was fired.
dataobjectEvent-specific payload. Shape varies by event type — see below.
05 — Signature Verification

Confirm the request is from us.

Your webhook endpoint is publicly accessible, so anyone could POST to it. If you set a Webhook Secret in your dashboard, ResilientLink includes it verbatim in the X-ResilientLink-Signature header on every delivery. Compare this value server-side to confirm the request is genuine.

The secret is sent as a plain string — no HMAC hashing. Store it in an environment variable and compare it directly. Return 401 if missing or mismatched.
Verification logicPseudocode
// On every incoming webhook request:
incomingSignature = request.headers["x-resilientlink-signature"]
expectedSecret   = ENV["RESILIENTLINK_WEBHOOK_SECRET"]

if incomingSignature != expectedSecret:
    return HTTP 401  // reject — not from ResilientLink

// safe to process
payload = parse_json(request.body)
handle(payload.event, payload.data)
06 — Implementation Examples

Working handlers in three languages.

Copy the example for your stack, replace the secret env var, and deploy. Your handler must return 200 OK — the response body is ignored entirely.

Node.js — Express JavaScript
const express = require('express');
const app     = express();
app.use(express.json());

app.post('/webhook', (req, res) => {
  // 1. Verify signature
  const sig    = req.headers['x-resilientlink-signature'];
  const secret = process.env.RESILIENTLINK_WEBHOOK_SECRET;
  if (!sig || sig !== secret) {
    return res.status(401).end();
  }

  // 2. Parse event
  const { event, timestamp, data } = req.body;
  console.log(`[webhook] Received: ${event}`);

  // 3. Route by event type
  if (event === 'scrape.completed') {
    // data.url, data.metadata.title, etc.
    handleScrapeCompleted(data);
  } else if (event === 'scrape.failed') {
    handleScrapeFailed(data);
  } else if (event === 'quota.warning') {
    handleQuotaWarning(data); // data.percent_used, data.threshold
  }

  // 4. MUST return 200 — body is ignored
  res.status(200).end();
});

app.listen(3000);
Python — Flask Python
from flask import Flask, request, abort
import os

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    # 1. Verify signature
    sig    = request.headers.get('X-ResilientLink-Signature')
    secret = os.environ['RESILIENTLINK_WEBHOOK_SECRET']
    if sig != secret:
        abort(401)

    # 2. Parse event
    payload   = request.get_json()
    event     = payload['event']
    data      = payload['data']
    timestamp = payload['timestamp']

    # 3. Route by event type
    if   event == 'scrape.completed': handle_completed(data)
    elif event == 'scrape.failed':    handle_failed(data)
    elif event == 'quota.warning':    handle_quota(data)

    # 4. MUST return 200
    return '', 200

if __name__ == '__main__':
    app.run(port=3000)
PHP PHP 8+
<?php
// webhook.php

// 1. Verify signature
$sig    = $_SERVER['HTTP_X_RESILIENTLINK_SIGNATURE'] ?? '';
$secret = getenv('RESILIENTLINK_WEBHOOK_SECRET');
if ($sig !== $secret) {
    http_response_code(401);
    exit();
}

// 2. Parse event
$payload = json_decode(file_get_contents('php://input'), true);
$event   = $payload['event']     ?? '';
$data    = $payload['data']      ?? [];

// 3. Route by event type
match($event) {
    'scrape.completed' => handle_completed($data),
    'scrape.failed'    => handle_failed($data),
    'quota.warning'    => handle_quota($data),
    default            => null,
};

// 4. MUST return 200
http_response_code(200);
exit();
Keep your handler fast. Do not run long-running tasks (database queries, external API calls, file processing) synchronously inside the handler. Acknowledge the webhook immediately with 200, then dispatch the work to a background queue or job processor.
07 — Quota Warning Event

Know before you hit the limit.

The quota.warning event fires when your usage crosses 80% or 95% of your effective monthly limit. It fires at most once per threshold per calendar month — you will not be spammed.

quota.warning — full payload JSON
{
  "event":     "quota.warning",
  "timestamp": "2026-05-19T18:42:00.000Z",
  "data": {
    "percent_used":   80,         // 80 or 95
    "requests_used":  800,        // requests consumed this month
    "requests_limit": 1000,       // effective limit (plan + bonus)
    "plan_limit":     1000,       // base plan allowance
    "bonus_requests": 0,          // remaining one-time bonus (referral)
    "threshold":      80,         // which threshold triggered this
    "reset":          "Resets on the 1st of next month."
  }
}
Handling quota warningsJavaScript
if (event === 'quota.warning') {
  const { percent_used, threshold, requests_used, requests_limit } = data;
  if (threshold === 95) {
    // Critical — switch to cached fallback or pause non-essential scrapes
    enableFallbackMode();
    alertOpsTeam(`CRITICAL: ${percent_used}% quota used`);
  } else {
    // Warning — notify team, consider upgrading
    sendSlackAlert(`Quota at ${percent_used}%: ${requests_used}/${requests_limit} used`);
  }
}
08 — Rules & Behaviour

What to expect in production.

RuleDetail
Must return 200 Any other status (201, 204, 4xx, 5xx) is treated as a failed delivery. The response body is never read — only the status code matters.
No retries ResilientLink does not automatically retry failed webhook deliveries. Implement idempotency in your handler in case you re-process events manually.
Inactive status skips delivery If your webhook status is set to Inactive in the dashboard, no events are fired at all — even if a URL is saved.
IP restriction applies at API & Webhook level The Allowed IPs setting blocks API requests — incoming webhooks . Your webhook endpoint can be on any IP or the specific IP you specified when enabled.
Secret header is optional If no secret is set, the X-ResilientLink-Signature header is omitted entirely. You can still receive events — but you lose the ability to verify origin.
Quota warnings are deduplicated The 80% and 95% thresholds each fire at most once per calendar month. The same threshold will not fire again until the counter resets on the 1st.
09 — Error Notifications

When delivery fails, you'll know.

If your webhook endpoint returns a non-200 response, or is completely unreachable, ResilientLink sends an email notification to your account email explaining the failure. To avoid inbox flooding, these emails are rate-limited to at most once per hour per failure type.

Two failure types
TypeCauseHow to fix
Non-200 response Your server is reachable but returned a status other than 200 (e.g. 500, 404, 204). Update your handler to always return exactly 200 OK, even if you don't process the event.
Unreachable endpoint The URL is invalid, the server is offline, DNS fails, or the connection is refused before a response is sent. Verify the URL is publicly accessible. Check server uptime, firewall rules, and DNS resolution.
A common mistake: returning 204 No Content thinking it means "success with no body." ResilientLink requires exactly 200. Always call res.status(200).end() explicitly — do not rely on framework defaults.
10 — Integration Checklist

Before you go live.