is-responding
v1.3.0
Published
π¨ A tool to find active endpoint use an enumeration strategy
Maintainers
Readme
is-responding
π¨ A tool to find active endpoints using an enumeration strategy

Give a βοΈ if this project helped you!
What it does
Plenty of services put a plain counter in the URL: /invoice/1042, /photo/007.jpg, /status/204. Point is-responding at such an address with the counter replaced by {{integer}}, give it a range, and it sends a HEAD request to every address in that range and prints the ones that answered.
is-responding -u "https://example.org/invoice/{{integer}}" -f 1000 -t 1100Five requests run in parallel by default, silent endpoints are skipped, and the exit code tells a script whether anything was found.
CLI
Installation:
npm install -g is-respondingOr run it without installing:
npx is-responding -u "https://example.org/{{integer}}"is-responding --helpUsage: is-responding -u <url with {{integer}}> [options]
Options:
--version Show version number [boolean]
--url, -u URL with {{parameter}} [required]
--from, -f Value the enumeration starts at [default: 0]
--to, -t Value the enumeration ends at [default: 10]
--pad, -p Pad values with leading zeros, 3 gives 007 [default: 0]
--concurrency, -c Requests kept in flight at once [default: 5]
--timeout Milliseconds before a request is abandoned [default: 10000]
--verbose, -v Display endpoints which refused
--help Show help [boolean]
Examples:
Scan a range of ids
is-responding -u "https://example.org/invoice/{{integer}}" -f 1000 -t 1100
Show why endpoints were skipped
is-responding -u "https://example.org/{{integer}}" -f 1 -t 50 --verbose
Fixed-width numbers, so 7 becomes 007
is-responding -u "https://example.org/photo/{{integer}}.jpg" -t 999 --pad 3
Scan a wide range faster
is-responding -u "https://example.org/{{integer}}" -t 5000 --concurrency 25
Keep the output in range order
is-responding -u "https://example.org/{{integer}}" -t 100 --concurrency 1
Exit codes:
0 at least one endpoint responded
1 nothing responded, or the arguments were invalid
130 the scan was interrupted with Ctrl+C
Press Ctrl+C to stop early; the report covers whatever was scanned.Options
| Option | Short | Default | Meaning |
| --- | --- | --- | --- |
| --url | -u | - | URL template containing at least one {{integer}}. Required. |
| --from | -f | 0 | First value of the range, inclusive. May be negative. |
| --to | -t | 10 | Last value of the range, inclusive. |
| --pad | -p | 0 | Width to pad values to with leading zeros. 0 disables padding. |
| --concurrency | -c | 5 | How many requests may be in flight at the same time. |
| --timeout | - | 10000 | Milliseconds before a single request is given up on. |
| --verbose | -v | off | Also print the endpoints that refused, with the reason. |
Every numeric option must be a whole number, and the run stops with exit code 1 if one is not.
Usage
Placeholders
A placeholder is written as {{type}} - double curly braces around a supported type name.
| Placeholder | Meaning | Supported |
| ------------- | --------------------------------------------- | --------- |
| {{integer}} | Every whole number from --from up to --to | β
Yes |
[!IMPORTANT]
integeris currently the only supported type, and the placeholder must be the type name itself. Invented names like{{id}},{{page}}or{{user}}are rejected - the tool prints"id" is not supportedand exits with code1.
A URL without any placeholder is rejected as well, because there would be nothing to enumerate. So is a template that is not a valid http/https address once the placeholders are filled.
Repeating a placeholder
You can use {{integer}} more than once. Each occurrence is an independent dimension, so the run walks the cartesian product of the ranges - not one shared counter.
is-responding -u "https://example.org/{{integer}}/photo/{{integer}}.jpg" -f 1 -t 3https://example.org/1/photo/1.jpg
https://example.org/1/photo/2.jpg
https://example.org/1/photo/3.jpg
https://example.org/2/photo/1.jpg
...
https://example.org/3/photo/3.jpgTwo placeholders side by side behave the same way - {{integer}}{{integer}} over 1..4 produces 11, 12, 13, 14, 21, 22, ... 44, so all 16 combinations, not just 11, 22, 33, 44.
[!WARNING] The number of requests is the range size raised to the number of placeholders. Two placeholders over
1..100means 10 000 requests, three means 1 000 000. Keep the range small when repeating a placeholder.
One placeholder or two?
Every placeholder shares the same --from/--to, so reach for a second one only when both positions really do span that range. A fixed-width number needs --pad, not a second placeholder:
# 31 requests: 01, 02 ... 31
is-responding -u "https://example.org/day-{{integer}}.pdf" -f 1 -t 31 --pad 2
# 961 requests: 0101, 0102 ... 3131, most of them meaningless
is-responding -u "https://example.org/day-{{integer}}{{integer}}.pdf" -f 1 -t 31 --pad 2When two positions need different ranges, such as a month and a day, keep one of them outside the template and loop:
for month in 01 02 03; do
is-responding -u "https://example.org/$month{{integer}}2026.pdf" -f 1 -t 31 --pad 2
doneParallel requests
Five requests are kept in flight at once. Each worker takes the next address as soon as its own finishes, so one slow endpoint holds up a single slot instead of the whole run.
is-responding -u "https://example.org/{{integer}}" -f 1 -t 500 --concurrency 20Raise it to finish sooner, lower it to go easy on the service. --concurrency 1 sends one request at a time, which keeps the output in range order.
[!NOTE] Results are printed as they arrive, so with parallel requests the order does not follow the range. Use
--concurrency 1when you want the output sorted.
Reading the output
While the scan runs, a progress bar tracks how far it has got. It lives on a single line and is wiped when the run ends, so it never pollutes the results.
ββββββββββββββββββββββββ 4/8 (50%)Every line that survives is a finding, which keeps the output greppable and safe to redirect.
| Line | Meaning |
| ------------------ | ------------------------------------------------------- |
| β 200 <url> | The endpoint answered, with the status it returned |
| β <reason> <url> | The endpoint refused or timed out, shown only with -v |
The run closes with a summary: how many endpoints were checked, how long it took, the throughput, the split between responding and silent, and how often each status came back.
Checked 8 endpoints in 1.9s (4.1/s)
Found 6 responding, 2 silent
Status breakdown:
no response 2
200 1
201 1
202 1Durations are rendered as 842ms below a second, 2.4s below a minute and 1m 35s above. no response covers the endpoints that never produced a status at all, such as a connection reset or a timeout. Only the ten most frequent statuses are listed; the rest are summed up as ... and N more.
The bar and the summary are written only when the output is a terminal; a redirected run emits the result lines and nothing else.
Stopping early
A long scan does not have to run to the end. Press Ctrl+C and the run stops accepting new endpoints, then prints the same report it would have printed on its own, covering everything checked up to that point.
Interrupted after 23 of 81 endpoints
Checked 23 endpoints in 2.9s (8.1/s)
Found 23 responding, 0 silent
Status breakdown:
200 23The exit code is 130, the shell convention for a run ended by Ctrl+C, so a script can tell a stopped scan from an empty one. The report appears straight away: requests already in flight get a moment to finish, and anything still hanging is abandoned rather than holding the summary back. Pressing Ctrl+C again kills the process outright.
Leading zeros
Some services expect a fixed-width number, like /photo/007.jpg. Use --pad (-p) to set that width:
is-responding -u "https://example.org/photo/{{integer}}.jpg" -f 7 -t 9 --pad 3https://example.org/photo/007.jpg
https://example.org/photo/008.jpg
https://example.org/photo/009.jpgValues already wider than the padding are left alone (--pad 2 keeps 1000 as 1000), and a negative value keeps its sign in front (--pad 3 turns -7 into -007).
Range
--from and --to are inclusive and may be negative. --from must not be greater than --to.
is-responding -u "https://example.org/{{integer}}" -f -3 -t 0Exit codes
Useful when calling the tool from a script or a CI job.
| Code | Meaning |
| ----- | ------------------------------------------------ |
| 0 | At least one endpoint responded |
| 1 | Nothing responded, or the arguments were invalid |
| 130 | The scan was interrupted with Ctrl+C |
if is-responding -u "https://example.org/{{integer}}" -f 1 -t 50; then
echo "found something"
fiExamples
β‘οΈ Use case: Find the live endpoints in a range
is-responding -u "https://httpbin.org/status/{{integer}}" -f 198 -t 205Scanning 8 endpoints with 5 parallel requests
β 200 https://httpbin.org/status/200
β 201 https://httpbin.org/status/201
β 202 https://httpbin.org/status/202
β 203 https://httpbin.org/status/203
β 204 https://httpbin.org/status/204
β 205 https://httpbin.org/status/205
Checked 8 endpoints in 1.9s (4.1/s)
Found 6 responding, 2 silent
Status breakdown:
no response 2
200 1
201 1
202 1
203 1
204 1
205 1β‘οΈ Use case: See why endpoints were skipped
Without --verbose a silent endpoint leaves no trace. Add -v to print the misses too, with the reason each one gave.
is-responding -u "https://httpbin.org/status/{{integer}}" -f 198 -t 201 -vScanning 4 endpoints with 5 parallel requests
β socket hang up https://httpbin.org/status/199
β socket hang up https://httpbin.org/status/198
β 200 https://httpbin.org/status/200
β 201 https://httpbin.org/status/201
Checked 4 endpoints in 748ms (5.3/s)
Found 2 responding, 2 silent
Status breakdown:
no response 2
200 1
201 1β‘οΈ Use case: Scan a wide range quickly
is-responding -u "https://example.org/{{integer}}" -f 1 -t 5000 --concurrency 25β‘οΈ Use case: Give up on slow endpoints faster
is-responding -u "https://example.org/{{integer}}" --timeout 2000API
The package can be used programmatically as well.
const { start } = require('is-responding');
const result = await start({
url: 'https://example.org/{{integer}}',
from: 1,
to: 20,
verbose: false,
timeout: 10000,
pad: 0,
concurrency: 5,
});
console.log(result.responding); // ['https://example.org/7', ...]
console.log(result.checked); // 20
console.log(result.silent); // 19
console.log(result.elapsed); // 2417 (milliseconds)
console.log(result.statuses); // { '200': 1, 'no response': 19 }
console.log(result.interrupted); // falsestart() resolves once every endpoint has been checked. It returns { responding, checked, silent, elapsed, statuses, interrupted }, or null when the URL template could not be enumerated.
Pass an AbortSignal to stop a scan from your own code. The returned report covers whatever was checked before the abort, exactly as it does for Ctrl+C:
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
const result = await start({
url: 'https://example.org/{{integer}}',
from: 1,
to: 100000,
verbose: false,
signal: controller.signal,
});
console.log(result.interrupted); // trueRelated
License
The MIT License @ 2026
