Webhook Sender IP Range Drift and Allowlist Gap Report avatar

Webhook Sender IP Range Drift and Allowlist Gap Report

Pricing

from $10.00 / 1,000 run starteds

Go to Apify Store
Webhook Sender IP Range Drift and Allowlist Gap Report

Webhook Sender IP Range Drift and Allowlist Gap Report

Fetch the webhook source IP ranges that SaaS providers such as GitHub, Stripe, Datadog, PagerDuty and Bitbucket publish, and compare them with the CIDR blocks that your firewall, WAF or ingress allows. The Actor reports the published ranges that your allo

Pricing

from $10.00 / 1,000 run starteds

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

This Actor finds the webhook source IP ranges that your firewall blocks.

Many SaaS providers send webhooks from published IP ranges. GitHub and Stripe are two examples. You allow these ranges at a firewall, a WAF or an ingress. When a provider adds a range and your allowlist does not, deliveries from the new range fail. The provider retries for some time and then stops. You do not get a message.

For each provider, the Actor does these steps:

  1. It gets the range list that the provider publishes (one HTTPS GET, at most 1 MB).
  2. It compares the list with the CIDR blocks that you allow.
  3. It compares the list with the list of the last run.

The Actor never sends a request to your webhook receiver. It reads only the public range lists.

What you get

  • Gap: the published ranges that your allowlist does not fully cover. Deliveries from these addresses are blocked. The row also gives the exact uncovered blocks inside each range.
  • New gap: the gap ranges that no earlier run reported. Only a new gap sends an alert and adds a charge.
  • Stale: the allowlist entries that touch no published range. The provider no longer uses these addresses, so you can remove them.
  • Added and removed: the ranges that the provider added or removed since the last run.
  • Gate: gate_pass is true only when the Actor read every list and your allowlist covers every published range.

Built-in provider catalog

We fetched each list on 2026-09-11 and it gave a machine-readable range list.

Preset IDSourceWhat the Actor reads
githubhttps://api.github.com/metaThe hooks field
stripehttps://stripe.com/files/ips/ips_webhooks.jsonThe WEBHOOKS field
datadog-us1https://ip-ranges.datadoghq.com/The webhooks IPv4 and IPv6 prefixes (US1 site)
buildkitehttps://api.buildkite.com/v2/metaThe webhook_ips field
pagerduty-ushttps://app.pagerduty.com/webhook_ipsThe list
pagerduty-euhttps://app.eu.pagerduty.com/webhook_ipsThe list
bitbuckethttps://ip-ranges.atlassian.com/The egress ranges for the bitbucket product
jirahttps://ip-ranges.atlassian.com/The egress ranges for the jira product
grafana-cloudhttps://grafana.com/api/hosted-alerts/source-ips.txtOne address on each line

A provider can move its list. If a preset fails, use a custom record with the new URL.

Input

{
"providers": [
"github",
"stripe",
{
"name": "acme",
"url": "https://acme.example.com/meta",
"format": "json_path",
"json_path": "webhooks.*"
}
],
"allowlist": {
"github": ["192.30.252.0/22", "185.199.108.0/22", "140.82.112.0/20", "143.55.64.0/20"],
"stripe": "3.18.12.63\n3.130.192.231",
"acme": ["203.0.113.0/24"]
},
"alert_webhook_url": "https://hooks.slack.com/services/T000/B000/XXXX",
"baseline_key": "prod-edge"
}
FieldDescription
providers1 to 20 items. Each item is a preset ID, or a custom record. A custom record has a name, one HTTPS url, a format (json_path or text_lines) and, for json_path, a json_path. The default is ["github", "stripe"].
allowlistAn object. The key is the provider name. The value is a list of IPv4 and IPv6 CIDR blocks, or one text with one block on each line. A single address is a /32 or /128 block. At most 2,000 blocks in total. The key is not case-sensitive.
alert_webhook_urlOptional. One public HTTPS URL. The Actor sends one JSON POST for each provider with a new gap.
baseline_keyThe record key of the baseline. Use one key for each allowlist, for example prod-edge and staging-edge. The default is DEFAULT.

The empty input checks GitHub and Stripe against the ranges that they published on 2026-09-11.

The JSON path

A JSON path is a list of segments with a dot between them:

  • hooks reads the field hooks.
  • * reads each item of a list, or each value of an object. webhooks.* reads all the lists inside webhooks.
  • ?key=value keeps the records whose key is value, or whose key is a list that contains value. items.?product=bitbucket.?direction=egress.cidr reads the cidr of each matching record.

A list at the end of the path is read item by item.

A provider without an allowlist

If a provider has no allowlist key, its verdict is no_allowlist. The Actor reports the drift of the provider, but it does not compare the list with an allowlist and does not charge for the provider. The gate fails, because the Actor cannot tell if your firewall allows the provider. An allowlist key that names no provider is shown in unused_allowlist_keys.

Output

The dataset has these records:

  • One summary record. It is the first record.
  • One provider record for each provider.
  • One gap_alert record for each provider with a new gap. This record is also the webhook payload.

A provider record:

{
"record_type": "provider",
"provider": "github",
"source_url": "https://api.github.com/meta",
"fetch_status": "OK",
"published_cidr_count": 6,
"baseline_status": "compared",
"added": ["2606:50c0::/32"],
"removed": [],
"gap": ["2606:50c0::/32"],
"gap_uncovered_blocks": ["2606:50c0::/32"],
"new_gap": true,
"new_gap_cidrs": ["2606:50c0::/32"],
"stale": ["192.30.0.0/24"],
"verdict": "gap",
"alert_status": "sent"
}
VerdictMeaning
coveredThe allowlist covers every published range.
gapOne or more published ranges are not fully covered.
no_allowlistThe provider has no allowlist key.
not_checkedThe Actor did not read the list. fetch_status and error give the cause.

fetch_status is one of OK, HTTP_ERROR, TIMEOUT, CONNECT_FAIL, DNS_FAIL, BLOCKED_TARGET, REDIRECT_ERROR, TOO_LARGE, PARSE_ERROR, EMPTY_LIST, INPUT_REJECTED or CHARGE_LIMIT. A list that gives no IP range is EMPTY_LIST. The Actor does not compare it, so an empty list never marks your whole allowlist as stale.

Each list in a record holds at most 500 items. The *_count fields always give the full count, and lists_truncated is true when a list was cut.

The summary record gives providers_total, providers_checked, providers_with_gap, providers_with_new_gap, providers_without_allowlist, providers_not_checked, gap_total, stale_total, unused_allowlist_keys and gate_pass.

A gap, a failed gate and an unreachable list are results. The run still ends with the status SUCCEEDED, and the status message gives the verdict. A bad input writes one input_error record and ends without a charge.

Use it on a schedule

A provider can add a range at any time and does not tell each customer. Create a Task with your input and run it on a daily Apify schedule. Set alert_webhook_url to get one POST when a new gap occurs.

State

The Actor keeps one record in the named key-value store webhook-ip-range-baseline, under the baseline_key. For each provider, the record holds the source URL, the last published range set and the gaps already reported. The added and removed lists need this record. The gap check does not need it.

  • The first run for a key writes the record. It reports each gap as new.
  • If the source URL of a provider changes, the Actor starts a new baseline for that provider.
  • If the Actor cannot read a list, it keeps the old baseline entry for that provider.
  • When a gap closes, the Actor forgets it. If the same range opens again, it is a new gap again.

Pricing: pay per event

EventUnitPrice (USD)
run-startedOne Actor run0.01
provider-range-checkedOne provider range list fetched and compared with the allowlist in one run0.01
allowlist-gap-flaggedOne provider with a new published range that the allowlist does not cover0.05
  • run-started is charged once, when the run starts.
  • provider-range-checked is charged for each provider with the verdict covered or gap. A list that the Actor did not read, a rejected record and a provider without an allowlist are not charged.
  • allowlist-gap-flagged is charged once for each provider with a new gap. A gap that an earlier run with the same baseline_key reported is not charged again.

Example: 5 providers on a daily schedule, with no new gap, cost 0.01 + 5 × 0.01 = USD 0.06 for each run, about USD 1.80 for each month.

The Actor reads your maximum total charge before it requests a list. When the limit covers only some providers, the Actor does not request the other lists. Their rows have the status CHARGE_LIMIT, and their baseline entries stay unchanged.

Limits and safety

  • At most 20 providers and 2,000 allowlist blocks.
  • One GET for each provider, with one retry for a timeout, a connection error, HTTP 429 or HTTP 5xx.
  • Each response is at most 1 MB and at most 10,000 entries. Each request has a 15-second timeout.
  • At most 3 redirects. Each redirect must use HTTPS.
  • 5 requests at the same time.
  • The Actor refuses a URL that resolves to a private or reserved address, before each request and each redirect.
  • GitHub allows 60 requests for each hour from one IP address without a token. On shared cloud IP addresses, the github preset can get HTTP 403. The row then gives HTTP_ERROR, the Actor retries on the next scheduled run, and the run is not charged for that provider.
  • The Actor does not use a browser, a proxy or a paid API.