Bulk Url Checker

io.github.carlosofsciencePublished by GitHub account carlosofsciencev1.0.1Updated Nov 1, 2026

Bulk HTTP status checker: up to 100,000 URLs per job, with redirect chains and response times.

VerifiedStreamable HTTPWeb executableWeb Search & ScrapingDeveloper Tools

Overview

AI-generated overview

Remote MCP server that lets an assistant submit large batches of URLs for HTTP status checks and retrieve status codes, redirects and response times.

What you can do
  • Submit lists of URLs for checking, returning results inline for small batches or a job ID for larger ones
  • Check job status and progress, then fetch paginated results with status code, response time and error message
  • List domains the account has verified, which are checked faster than unverified ones
  • Check how many URL checks remain on the account
When to use it
Useful for bulk link auditing, broken-link detection and redirect or response-time checks across large URL lists, where a managed service with proxies and per-domain throttling saves building your own checker.
Requirements
A remote endpoint at mcp.bulkurlchecker.com over streamable HTTP; no authentication is declared in the manifest, but the service is account-based and the README describes API keys and a paid quota system. Domain verification, if wanted, is done in the web dashboard via DNS TXT record or meta tag.
Before you install
Submitted URLs are sent to a third-party cloud service that fetches them through residential proxies. Checks consume a metered quota; plans run from $9 to $99 per month after free allowances, and credit packs cost money. Account-level API keys and webhook signing secrets are described in the README and should be kept private.

Installation

In SourceWeft

  1. Open Bulk Url Checker in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.carlosofscience%2Fbulk-url-checker/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "bulk-url-checker": {
      "type": "http",
      "url": "https://mcp.bulkurlchecker.com/mcp"
    }
  }
}

README

bulkurlchecker

[PyPI version] [Python versions] [License: MIT]

Python client for the Bulk URL Checker API.

Skip the proxy-rotation, rate-limiter, soft-404 detector, and retry classifier you would otherwise spend two weeks building. Submit thousands of URLs, get status codes, redirect chains, and broken-link detection back as plain Python objects. Backed by a managed cloud service with residential proxies and per-domain throttling.

Install

bash
pip install bulkurlchecker

5-line example

python
from bulkurlchecker import Client
client = Client(api_key="uck_live_...")results = client.check_urls(["https://example.com", "https://example.org"])for r in results.results:    print(r.url, r.status_code, "BROKEN" if r.is_broken else "ok")

Get an API key at https://app.bulkurlchecker.com/dashboard/api-keys. First 300 URLs are free, no card required.

What you get back

python
results = client.check_urls(urls)
results.status            # 'completed' | 'paused' | 'failed' | 'cancelled'results.timed_out         # True if the wait deadline passed (job still running)results.total_urls        # how many URLs the engine acceptedresults.completed_urls    # how many it finished checkingresults.duplicates_removedresults.invalid_urls_rejected
for r in results.results:    r.url                # the original URL you submitted    r.final_url          # after redirects    r.status_code        # 200, 301, 404, 429, 500, ...    r.redirect_chain     # list of intermediate URLs    r.is_broken          # True if the engine flagged this as broken    r.is_soft_404        # True if 200 OK but page content says "not found"    r.response_time_ms
# Convenience properties:results.broken           # list of URLResult where is_broken == Trueresults.soft_404s        # list where is_soft_404 == True

Command-line usage

Install with the [cli] extra to get the bulkurlchecker command:

bash
pip install 'bulkurlchecker[cli]'export BULKURLCHECKER_API_KEY=uck_live_...
# One URL per line in urls.txt → CSV on stdoutbulkurlchecker check urls.txt > report.csv
# Pipe from anywheresitemap-extractor mydomain.com | bulkurlchecker check - --output jsonl > report.jsonl
# Only show broken URLs, ad-hoc inputbulkurlchecker check --urls "https://example.com,https://example.org" --only-broken
# Two-step (submit, then poll & fetch later)JOB=$(bulkurlchecker submit urls.txt)bulkurlchecker status "$JOB"bulkurlchecker results "$JOB" --output csv > report.csv

Output formats: csv (default), json, jsonl. Run bulkurlchecker check --help for the full flag list.

Larger jobs: submit and poll

check_urls() blocks for up to 15 minutes server-side. For lists where the wait would time out, use the two-step pattern:

python
job = client.submit(my_500k_urls)print(f"Submitted {job.job_id}, {job.total_urls} URLs queued")
# Poll explicitly, or use the convenience methoddone = client.wait_until_done(job.job_id, timeout=3600)
# Stream results in pagesfor batch in client.iter_results(job.job_id, page_size=1000):    for r in batch:        if r.is_broken:            print(r.url, r.status_code)

Safe retries with idempotency_key

Pass an idempotency key on submit() or check_urls() to make retries safe under network failures. The server caches the response for 24 hours; a retry with the same key + same body returns the original result without creating a duplicate job.

python
import uuid
key = str(uuid.uuid4())  # generate once per logical request
# First call: creates a new job.job = client.submit(urls, idempotency_key=key)
# Network blip, no clean response received? Just retry with the same# key -- you'll get the SAME job summary back, no duplicate submission.same_job = client.submit(urls, idempotency_key=key)# job.job_id == same_job.job_id

Same idempotency_key + different urls returns 409 Conflict (raised as ValidationError) so client bugs that reuse a key against a new payload are caught loudly instead of silently mapping to the wrong cached response.

Receiving webhooks

When a job finishes, we POST to your registered endpoint with a signed payload. Verify the signature before trusting the body — anyone who knows your endpoint URL can otherwise send fake events.

python
from flask import Flask, requestfrom bulkurlchecker import verify_signature, InvalidSignatureError
SECRET = os.environ["MY_WEBHOOK_SECRET"]  # the signing_secret we showed once
app = Flask(__name__)
@app.route("/webhook/bulkurlchecker", methods=["POST"])def webhook():    try:        verify_signature(            request.get_data(),  # RAW bytes — not request.get_json()            request.headers.get("Bulkurlchecker-Signature", ""),            SECRET,        )    except InvalidSignatureError:        return "", 401    event = request.get_json()    if event["type"] == "job.completed":        job_id = event["data"]["job_id"]        # ... fetch results, update your DB, ping Slack, etc ...    return "", 200

Register endpoints + get secrets at https://app.bulkurlchecker.com/dashboard/webhooks (or via POST /api/v2/webhooks/endpoints).

verify_signature() enforces a 5-minute timestamp tolerance by default to defeat replays. Tune via tolerance_seconds=.

Error handling

All errors derive from BulkURLCheckerError. Catch specific subclasses when you want to branch on the failure mode:

python
from bulkurlchecker import (    Client,    BulkURLCheckerError,    AuthenticationError,    RateLimitError,    QuotaError,    ValidationError,)
try:    results = client.check_urls(urls)except QuotaError as e:    print(f"Out of credits. Top up at https://app.bulkurlchecker.com/billing")except RateLimitError as e:    print(f"Rate limited. Retry after {e.retry_after}s.")except AuthenticationError:    print("API key rejected — check it's not revoked.")except ValidationError as e:    print(f"Bad request: {e}")  # bad URLs, too many URLs, etc.except BulkURLCheckerError as e:    print(f"Other error: {e} (request_id={e.request_id})")

Every error carries status_code, code (server's machine-readable string), request_id (for support), and details (when the server provides them).

Why use this instead of writing your own checker with httpx + asyncio?

Honest answer: for ≤500 URLs you don't need this. The standard requests/httpx toolchain handles it fine.

The wall hits at scale:

ProblemRolling your ownThis SDK
Concurrencyasyncio + careful semaphoresdone
Proxy rotation across residential IPs$90+/mo Webshare / Bright Data subscription + custom codedone
Per-domain rate limiting (so you don't hammer one host)wire it yourselfdone
Distinguishing real 403 from "you got blocked" 403guess and checkdone
Detecting soft 404s (200 OK + "not found" body)regex / heuristic per templatedone
Retry classification (transient vs permanent)tune for weeksdone
Long-running job state (resume after crash)Redis + queue + worker infradone
Engineer time, weeks 1-4$$$nothing, ship today

If you've already lost a weekend to httpx + proxy rotation, you know what we're talking about.

Pricing

  • First month on us: 15,000 URL checks on the Starter plan, no credit card. The first time an API or SDK integration runs successfully, the trial grows to a month of Pro: 50,000 URL checks, once per account.
  • After that: 300 URL checks a month are included on every account.
  • Starter: $9/month or $90/year, 15,000 URLs/month
  • Pro: $29/month or $290/year, 50,000 URLs/month
  • Agency: $99/month or $990/year, 200,000 URLs/month

Scheduled checks, with email, Slack and webhook alerts, are included on every plan. One-time credit packs start at $5 for 3,000 checks, and pack credits roll over. Current prices: https://bulkurlchecker.com/#pricing

Links

Stability

The SDK follows semver. While we're at 0.x, breaking changes can land in minor releases (we'll always note them in CHANGELOG.md). Once we hit 1.0 you can pin major versions safely.

License

MIT. See LICENSE.

Source: README.md at commit c9b4cf9

Tools

5

submit_urls

Submit URLs for checking

Submits a list of URLs to be checked and returns a job_id. Lists of up to about 200 URLs complete inline, waiting up to 60 seconds, and the results are returned directly. Larger lists return immediately with the job_id alone, and their results become available as the job progresses.

Parametersurlsrequired

get_job_status

Get job status

Get the current status and progress of a job.

Parametersjob_idrequired

get_job_results

Get job results

Fetch paginated results for a job. Each result has the URL, HTTP status code (null if unreachable), response time in ms, and an error message if the check failed.

Parameterslimitjob_idrequiredoffset

get_verified_domains

Get verified domains

Lists the domains this account has proven ownership of. Checks against a verified domain run substantially faster than checks against an unverified one. Verification is interactive, requiring a DNS TXT record or a meta tag, and is done in the web dashboard rather than through this API.

Takes no parameters.

get_usage

Get credit balance

Returns the number of URL checks currently available on this account.

Takes no parameters.

Version history

2
  1. v1.0.1LatestOct 18, 2026
  2. v1.0.0Sep 16, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts