Skip to main content

bulk_http

A massive asynchronous, multiprocess HTTP execution engine for Python.

bulk_http sends, analyses and conditionally filters millions of HTTP requests in minimal time, while keeping memory usage constant regardless of input size. It pairs a native C networking core (libcurl-impersonate via curl_cffi) with browser-fingerprint impersonation, multiprocess distribution, on-the-fly response evaluation, proxy management and crash-safe, resumable output.

Why

Standard Python HTTP stacks collapse at the multi-million request scale:

  • Thread pools over synchronous clients drown the OS in context switches.
  • Loading whole input files or accumulating results in memory ends in OOM.
  • Full response bodies are downloaded even when a few kilobytes would do.
  • Default TLS/HTTP2 fingerprints are trivially detected and blocked.

bulk_http addresses each of these with lazy streaming ingestion, early network abort, a compiled networking core and process-level parallelism. Memory scales with concurrency × workers, never with input size.

Installation

pip install bulk_http

Requires Python 3.11+. On POSIX, uvloop is used automatically when available.

Quick start

from bulk_http import Engine, sources

if __name__ == "__main__":  # required under the spawn start method
    engine = Engine(workers="auto", impersonate="chrome", stream_cut=15_000)
    summary = engine.run(sources.text("targets.txt"), needle="admin", out_dir="out")
    print(summary)

Results are written as newline-delimited JSON (one file per worker) under out/. Re-running with the same checkpoint= resumes where a run left off.

Sources

All sources stream lazily — the whole input is never held in memory:

from bulk_http import sources

sources.memory(["https://a.com", "https://b.com"])  # list of URLs or Request objects
sources.text("urls.txt")  # one URL per line
sources.csv("targets.csv", mapping={"url": 0, "needle": 1, "keep": [2, 3]})
sources.tsv("targets.tsv", mapping={"url": 0})
sources.json("targets.jsonl")  # one JSON object per line

The CSV/TSV mapping links request attributes to column indices; "keep" preserves extra columns as metadata (reinjected into the output). A Request object can override any engine default per line (method, headers, cookies, body, needle, proxy, stream-cut, …).

Filtering

Conditions combine with AND; a response is kept only if all configured checks pass:

  • StatusRequest(expected_status=200) or a set frozenset({200, 301}).
  • Keyword — a global needle= on run, or per-URL needles= on a request; matched with Aho-Corasick over the decompressed fragment.
  • Predicate — a callable over a typed context:
engine.run(
    source,
    predicate=lambda ctx: ctx.type == "json" and ctx.data.get("admin") is True,
)

The predicate context exposes type (json/html/text), data (parsed JSON), dom (a selectolax HTML tree), text, status, headers, url and meta.

Reading less: Fast-Status and Stream-Cut

  • fast_status=True — abort after headers, never reading the body.
  • stream_cut=15_000 — abort once 15 KB have been received. cut_on="wire" (default) counts received (compressed) bytes to minimise proxy bandwidth; cut_on="decoded" counts decompressed bytes. Truncated fragments are decompressed tolerantly before searching.

Proxies

from bulk_http import Engine, sources

engine.run(source, proxies=sources.proxy_pool("proxies.txt"))

The pool round-robins over healthy proxies; a per-proxy circuit breaker quarantines a proxy on a sustained transport-failure rate and backs off on 429, while origin statuses (403/5xx) are blamed on the target, not the proxy. A fixed proxy can also be set per request via Request(proxy=...), and a rotating gateway with sticky sessions is available (bulk_http.proxies.BackconnectGateway).

Crash-safe output and resume

Each worker appends to its own NDJSON file. A batch is only committed to the checkpoint manifest after its output is flushed and fsynced, so a crash never loses committed data or double-counts it. Resuming truncates each file back to its committed offset and replays only the pending batches:

engine = Engine(checkpoint="campaign.ckpt")
engine.run(source, out_dir="out")  # interrupt any time…
engine.run(source, out_dir="out")  # …and re-run to resume exactly once

Convert the NDJSON to CSV at the end with bulk_http.sinks.export_csv.

Metrics

run returns a RunSummary whose metrics field aggregates the campaign across all workers — total, match count, status distribution, error counts and latency percentiles (p50/p95/p99) — with bounded memory. Pass metrics_callback= to receive the snapshot at the end of the run:

summary = engine.run(source, metrics_callback=lambda m: print(m.as_dict()))
print(summary.metrics.p95, summary.metrics.by_status)

Timeouts

timeout bounds a single attempt; total_timeout bounds the whole per-URL budget (attempts plus backoffs) — once the deadline would be crossed, no further attempt or backoff is started.

Responsible use

bulk_http is intended for use within an authorised scope (assets you own, bug-bounty programs with an explicit scope, internet-measurement research). It provides the technical means for compliant use:

  • denylist= / allowlist= of domains (matching a host and its subdomains).
  • per_domain_rate_limit= requests per second per host.
  • identity_header=("X-Contact", "you@example.com") for a contactable scan.
  • authorization="scope reference" attached to every output record.
  • respect_robots=True — enforce robots.txt per host (disallowed URLs are skipped with a robots_disallowed result) and honour its crawl-delay. Disabled by default.

Legal responsibility for how the library is used rests with the user.

HTTP/3

HTTP/3 is opt-in per request via http_version="h3" and requires a QUIC-capable libcurl build (detected at runtime) and a UDP-capable proxy for proxied traffic; the default ("auto") negotiates HTTP/2 or HTTP/1.1.

Platform notes

Windows is a first-class target. Because its selector event loop caps sockets per process, concurrency is reached by scaling out worker processes rather than raising per-process concurrency; the reliable Selector loop is the default, with an opt-in winloop path pending validation.

Documentation

A full, task-oriented guide — sources, filtering, concurrency, proxies, rate limiting, crash-safe resume, metrics, responsible use and troubleshooting — is in docs/TUTORIAL.md. Runnable scripts live in examples/.

Development

uv sync --extra dev
./scripts/check.sh   # ruff lint + format check, mypy (strict), pytest with coverage

See examples/ for runnable scripts.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bulk_http-0.1.0.tar.gz (80.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bulk_http-0.1.0-py3-none-any.whl (58.1 kB view details)

Uploaded Python 3

File details

Details for the file bulk_http-0.1.0.tar.gz.

File metadata

  • Download URL: bulk_http-0.1.0.tar.gz
  • Upload date:
  • Size: 80.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for bulk_http-0.1.0.tar.gz
Algorithm Hash digest
SHA256 da57bac37507833ddfa4447b12d3c2e40e50096e120612f169eae2fc9eea271c
MD5 ddaab3db70aee9145f24eec5f6ddd1d7
BLAKE2b-256 6ea8c2823324400aa4b7a810ccb15bfde39bb163fbb03789d794dc72afc6967b

See more details on using hashes here.

Provenance

The following attestation bundles were made for bulk_http-0.1.0.tar.gz:

Publisher: publish.yml on linoMlv/bulk_http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bulk_http-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: bulk_http-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 58.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for bulk_http-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 695e216ab38c43051d6b2322ceedba3c8faebc564bc5f6073eb08c872508269b
MD5 eb303b35829418b5e6eb2b532fff2da1
BLAKE2b-256 56aa64d25e4dcdda2e2e68f076ca47fe8685f0512056a2634b6e5c058d550959

See more details on using hashes here.

Provenance

The following attestation bundles were made for bulk_http-0.1.0-py3-none-any.whl:

Publisher: publish.yml on linoMlv/bulk_http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page