Skip to content

API reference

Generated from the source. The names below are re-exported from the top-level torquests package.

Functional API

api

The module-level functional API, mirroring requests.

Each verb runs through an ephemeral :class:Session over a process-global :class:TorClient. The client bootstraps a verified consensus of the Tor network on first use and is shared across calls, so repeated requests do not re-bootstrap.

request(method: str, url: str, **kwargs: Any) -> requests.Response

Send a request over Tor and return the response.

get(url: str, **kwargs: Any) -> requests.Response

Send a GET request over Tor. See :func:request.

head(url: str, **kwargs: Any) -> requests.Response

Send a HEAD request over Tor (redirects off by default). See :func:request.

post(url: str, **kwargs: Any) -> requests.Response

Send a POST request over Tor. See :func:request.

put(url: str, **kwargs: Any) -> requests.Response

Send a PUT request over Tor. See :func:request.

patch(url: str, **kwargs: Any) -> requests.Response

Send a PATCH request over Tor. See :func:request.

delete(url: str, **kwargs: Any) -> requests.Response

Send a DELETE request over Tor. See :func:request.

options(url: str, **kwargs: Any) -> requests.Response

Send an OPTIONS request over Tor. See :func:request.

new_identity() -> None

Rotate the global client's circuits, if it has been created.

close() -> None

Tear down the global client (mainly for tests and clean shutdown).

Sessions

Session(*, tor: TorConnector | None = None, isolation: IsolationPolicy = 'host', isolation_token: object | None = None, onion_auth: Mapping[str, bytes] | None = None)

Bases: Session

A requests session whose transport is Tor.

new_identity() -> None

Rotate circuits and clear session state so later requests are unlinkable.

Drops the pooled circuits, so subsequent requests take fresh paths and exits, and clears the cookie jar, so a site cannot relink the new identity to the old one through a stored cookie.

MixedSession(*, tor: TorConnector | None = None, isolation: IsolationPolicy = 'host', isolation_token: object | None = None, onion_auth: Mapping[str, bytes] | None = None)

Bases: Session

Routes .onion hosts through Tor and clearnet hosts directly.

Clearnet requests leave over the real IP by design. So that an onion browsing session does not leak, a redirect from an onion service to a clearnet host is refused rather than followed directly (see :meth:send).

send(request: requests.PreparedRequest, **kwargs: Any) -> requests.Response

Send a request, refusing any onion-to-clearnet redirect hop.

requests follows redirects by re-entering send once per hop. This remembers the previous hop's host, so a hop that crosses from an onion service to a clearnet host is rejected with :class:~torquests.exceptions.OnionRedirectError before it can be fetched directly over the real IP. The check is per-hop, so it holds at any point in a redirect chain, not only when the chain began at an onion service.

Client

TorClient(config: TorConfig | None = None, *, path_provider: PathProvider, transport_factory: TransportFactory | None = None, directory: LiveDirectory | None = None, onion_auth: Mapping[str, bytes] | None = None)

Owns guard channels and a circuit pool; hands out connected streams.

bootstrap(config: TorConfig | None = None, *, timeout: float = 60.0, onion_auth: Mapping[str, bytes] | None = None) -> TorClient classmethod

Build a client on a live, verified consensus of the Tor network.

Fetches the consensus once per process (shared across clients) and selects real relays over TLS. The consensus and key-certificate fetches are not anonymized, but the subsequent per-circuit microdescriptor fetches are tunneled over Tor (see :meth:_install_dir_tunnel), and the traffic the client then carries always is. onion_auth maps an onion host to the x25519 client-authorization private key used to reach an authorized-only v3 service.

new_identity() -> None

Drop all pooled circuits so subsequent requests take fresh paths.

TorConfig(connect_timeout: float = 60.0, read_timeout: float | None = 60.0, circuit_build_attempts: int = 3, cache_dir: Path | None = None) dataclass

Tunable parameters for a :class:~torquests._client.torclient.TorClient.

Transport

TorAdapter(connector: TorConnector, *, isolation: IsolationPolicy = 'host', owns_connector: bool = False, isolation_token: object | None = None)

Bases: BaseAdapter

Routes requests traffic over Tor.

IsolationPolicy = Literal['session', 'host', 'request'] module-attribute

Stealth

stealth_session(*, impersonate: str = DEFAULT_IMPERSONATE, tor: TorConnector | None = None) -> requests.Session

Return a :class:requests.Session that speaks a browser's TLS over Tor.

Requests go out with curl_cffi's impersonated TLS/HTTP fingerprint (Tor Browser by default) tunneled through torquests' Tor circuits, and come back as ordinary :class:requests.Response objects. Pass impersonate to choose another profile (for example "firefox" or "chrome"), or tor to reuse an existing client. Circuits are isolated per destination host. Needs the torquests[stealth] extra.

StealthTorAdapter(*, impersonate: str = DEFAULT_IMPERSONATE, tor: TorConnector | None = None, connect_timeout: float = 60.0)

Bases: BaseAdapter

A requests adapter that speaks a browser's TLS fingerprint over Tor.

Each request is issued with curl_cffi (impersonating impersonate) through an in-process SOCKS5-over-Tor proxy on 127.0.0.1, then repackaged as a :class:requests.Response. Names, .onion included, resolve at the exit (socks5h), so nothing reaches the local resolver. The proxy isolates circuits per destination host.

Exceptions

exceptions

Exception hierarchy.

Every Tor-specific error subclasses the marker :class:TorError and the matching requests exception, so code written against requests keeps working (except requests.exceptions.ConnectionError) while callers who want to distinguish Tor failures can catch :class:TorError. This mirrors how requests itself composes exceptions (for example ConnectTimeout is both a ConnectionError and a Timeout).

This module imports only requests, so it sits outside the package's internal layering and may be raised from anywhere.

TorError

Bases: RequestException

Base class for every error raised by torquests.

TorBootstrapError

Bases: TorError, ConnectionError

The client could not bootstrap a usable view of the Tor network.

ConsensusError

Bases: TorBootstrapError

The network consensus could not be fetched, parsed, or verified.

DirectoryError

Bases: TorBootstrapError

A directory document (microdescriptors, certificates) could not be fetched.

ChannelError

Bases: TorError, ConnectionError

The TLS link to a guard relay failed.

LinkAuthError

Bases: ChannelError

The guard's CERTS chain did not authenticate its identity.

CircuitError

Bases: TorError, ConnectionError

A circuit could not be built, extended, or used.

CircuitBuildTimeout

Bases: CircuitError, ConnectTimeout

A circuit did not finish building within the connect budget.

CircuitDestroyed

Bases: CircuitError

A DESTROY cell tore the circuit down while it was in use.

StreamError

Bases: TorError, ConnectionError

A stream failed abnormally.

StreamConnectError

Bases: StreamError

RELAY_BEGIN was refused (exit policy, unreachable host, or no route).

StreamConnectTimeout

Bases: StreamError, ConnectTimeout

RELAY_BEGIN did not produce a RELAY_CONNECTED within the connect budget.

A timeout is a distinct failure from a refusal, so it maps to the requests ConnectTimeout (a ConnectionError and a Timeout) rather than to a bare :class:StreamConnectError.

TorReadTimeout

Bases: TorError, ReadTimeout, TimeoutError

No data arrived on a stream within the read budget.

It also subclasses :class:TimeoutError (socket.timeout) so that when it surfaces while urllib3 is reading a response body, urllib3's _error_catcher classifies it as a read timeout (ReadTimeoutError) exactly as it would a real socket timeout, which requests then maps to the same exception a vanilla body-read timeout produces instead of a ChunkedEncodingError.

TorTLSError

Bases: TorError, SSLError

The TLS handshake with the destination host failed.

HTTPS-over-Tor runs the destination handshake in TlsStreamSocket. A failure there (a certificate that does not verify, a plaintext port answering the ClientHello, a protocol-version mismatch) raises a bare ssl.SSLError, which is not a requests exception. Wrapping it keeps the hierarchy's contract: a caller's except requests.exceptions.SSLError and except TorError both catch it, exactly as they would a direct requests HTTPS failure.

OnionServiceError

Bases: TorError, ConnectionError

A v3 onion-service connection failed.

DescriptorError

Bases: OnionServiceError

An onion-service descriptor could not be fetched, verified, or decrypted.

DescriptorNotFound

Bases: DescriptorError

No responsible HSDir returned a descriptor for the address.

IntroductionError

Bases: OnionServiceError

The introduction step failed for every introduction point.

RendezvousError

Bases: OnionServiceError

The rendezvous step failed (no RENDEZVOUS2, or the handshake did not verify).

OnionClientAuthRequired

Bases: OnionServiceError

The descriptor is client-authorized and no matching key was supplied.

InvalidOnionAddress

Bases: TorError, InvalidURL

A .onion address failed length, version, checksum, or torsion validation.

ProxyUnsupportedError

Bases: TorError, InvalidSchema

A proxies argument was passed to the Tor adapter; Tor is the transport.

OnionRedirectError

Bases: TorError

A :class:~torquests.MixedSession refused a redirect from an onion service to a clearnet host, which would have been fetched directly over the real IP.