Skip to main content

Smart, policy-driven query string merging & encoding for httpx powered by qs-codec.

PyPI version PyPI - Status PyPI - Downloads Supported Python versions PyPy support status PyPI - Format Tests CodeQL License Codecov Codacy Quality GitHub Sponsors GitHub Repo stars Contributor Covenant mypy pylint isort Security Status

Overview

httpx-qs provides:

  • A transport wrapper SmartQueryStrings that merges existing URL query parameters with additional ones supplied via request.extensions.

  • A flexible merge_query utility with selectable conflict resolution policies.

  • Consistent, standards-aware encoding via qs-codec (RFC3986 percent-encoding, structured arrays, nested objects, etc.).

Why?

HTTPX already lets you pass params= when making requests, but sometimes you need to:

  • Inject additional query parameters from middleware/transport layers (e.g., auth tags, tracing IDs, feature flags) without losing the caller’s original intent.

  • Combine repeated keys or treat them deterministically (replace / keep / error) rather than always flattening.

  • Support nested data or list semantics consistent across clients and services.

qs-codec supplies the primitives (decoding & encoding with configurable ListFormat). httpx-qs stitches that into HTTPX’s transport pipeline so you can declaratively extend queries at request dispatch time.

Requirements

  • CPython 3.8-3.14, including free-threaded 3.14t, or PyPy 3.8-3.11

  • httpx>=0.28.1,<1.0.0

  • qs-codec>=1.5.0

Installation

pip install httpx-qs

Minimal Example

import httpx
from httpx_qs.transporters.smart_query_strings import SmartQueryStrings

client = httpx.Client(transport=SmartQueryStrings(httpx.HTTPTransport()))

response = client.get(
        "https://www.google.com",
        params={"a": "b", "c": "d"},
        extensions={"extra_query_params": {"c": "D", "tags": ["x", "y"]}},
)

print(str(response.request.url))
# Example (order may vary): https://www.google.com/?a=b&c=d&c=D&tags=x&tags=y

Using Merge Policies

Conflict resolution when a key already exists is controlled by MergePolicy.

Available policies:

  • combine (default): concatenate values → existing first, new afterward (a=1&a=2)

  • replace: last-wins, existing value is overwritten (a=2)

  • keep: first-wins, ignore the new value (a=1)

  • error: raise ValueError on duplicate key

Specify per request:

from httpx_qs import MergePolicy

r = client.get(
        "https://api.example.com/resources",
        params={"dup": "original"},
        extensions={
                "extra_query_params": {"dup": "override"},
                "extra_query_params_policy": MergePolicy.REPLACE,
        },
)
# Query contains only dup=override

Async Usage

SmartQueryStrings works equally for AsyncClient:

import httpx
from httpx_qs.transporters.smart_query_strings import SmartQueryStrings

async def main() -> None:
        async with httpx.AsyncClient(transport=SmartQueryStrings(httpx.AsyncHTTPTransport())) as client:
                r = await client.get(
                        "https://example.com/items",
                        params={"filters": "active"},
                        extensions={"extra_query_params": {"page": 2}},
                )
                print(r.request.url)

# Run with: asyncio.run(main())

merge_query Utility

You can use the underlying function directly:

from httpx_qs import merge_query, MergePolicy
from qs_codec import EncodeOptions, ListFormat

new_url = merge_query(
        "https://example.com?a=1",
        {"a": 2, "tags": ["x", "y"]},
        options=EncodeOptions(list_format=ListFormat.REPEAT),
        policy=MergePolicy.COMBINE,
)
# → https://example.com/?a=1&a=2&tags=x&tags=y

Why ListFormat.REPEAT by Default?

qs-codec exposes several list formatting strategies (e.g. repeat, brackets, indices). httpx-qs defaults to ListFormat.REPEAT because:

  • It matches common server expectations (key=value&key=value) without requiring bracket parsing logic.

  • It preserves original ordering while remaining unambiguous and simple for log inspection.

  • Many API gateways / proxies / caches reliably forward repeated keys whereas bracket syntaxes can be normalized away.

If your API prefers another convention (e.g. tags[]=x&tags[]=y or tags[0]=x) just pass a custom EncodeOptions via extensions['extra_query_params_options'] or parameter options when calling merge_query directly.

Advanced Per-Request Customization

from qs_codec import EncodeOptions, ListFormat

r = client.get(
        "https://service.local/search",
        params={"q": "test"},
        extensions={
                "extra_query_params": {"debug": True, "tags": ["alpha", "beta"]},
                "extra_query_params_policy": "combine",  # also accepts string values
                "extra_query_params_options": EncodeOptions(list_format=ListFormat.BRACKETS),
        },
)
# Example: ?q=test&debug=true&tags[]=alpha&tags[]=beta

Error Policy Example

try:
        client.get(
                "https://example.com",
                params={"token": "abc"},
                extensions={
                        "extra_query_params": {"token": "xyz"},
                        "extra_query_params_policy": "error",
                },
        )
except ValueError as exc:
        print("Duplicate detected:", exc)

Testing Strategy

The project includes unit tests covering policy behaviors, error handling, and transport-level integration. Run them with:

pytest

Further Reading

License

BSD-3-Clause. See LICENSE for details.

Contributing

Issues & PRs welcome. Please add tests for new behavior and keep doc examples in sync.

Metadata

Release files for httpx-qs 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for httpx-qs 0.2.2
File Size Uploaded Provenance
httpx_qs-0.2.2.tar.gz 12.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for httpx-qs 0.2.2
File Interpreter ABI Platform
httpx_qs-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 21.1 kB

Release files / httpx_qs-0.2.2.tar.gz

Download URL httpx_qs-0.2.2.tar.gz
Size 12.3 kB
Tags Source
SHA-256 checksum
How to use checksums
2049c04ba17dcdd71ff0e193c5cd8142ff7988776d54ab0576623c46e5e035c7
BLAKE2b-256 checksum
How to use checksums
fbadc8c73dbe411b5daa56286ba4f47ce1545572e1fbf01ae27a433899b577d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.

Transparency log

Release files / httpx_qs-0.2.2-py3-none-any.whl

Download URL httpx_qs-0.2.2-py3-none-any.whl
Size 8.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fbb1dce4f1c807daa69b656bd54cff81416ec3fefaa8c5fb3c160fa670c3a1ca
BLAKE2b-256 checksum
How to use checksums
9167262d1a73b59f63c2e529a343efd55c1c2d172259d8564b12d4ae6ec3adfc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.

Transparency log

Security for httpx-qs 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for httpx_qs-0.2.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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