Type-safe query builder for Brave Search operators
Project description
bsqb
bsqb (Brave Search Query Builder) is a type-safe, zero-dependency Python library for constructing Brave Search query strings using the official search operators.
It helps you build valid q parameters for the Brave Search API with a fluent API, automatic quoting, and validation against API limits.
Features
- Complete operator coverage — All operators from the official Brave Search documentation
- Fluent API — Chain methods for readable, composable queries
- Type-safe — Full type hints and
py.typedmarker for mypy/Pylance - Zero dependencies — Lightweight core with no runtime requirements
- API limit validation — Enforces the 400 character / 50 word limits on
q - Logical operators —
AND,OR,NOTwith Python operators (&,|,~)
Installation
pip install bsqb
Quick start
from bsqb import Query
# Basic query with field operators
query = Query("machine learning").filetype("pdf").lang("en")
print(query.build())
# machine learning filetype:pdf lang:en
# Use with the Brave Search API
import urllib.parse
import urllib.request
params = urllib.parse.urlencode({"q": query.build()})
url = f"https://api.search.brave.com/res/v1/web/search?{params}"
request = urllib.request.Request(
url,
headers={"X-Subscription-Token": "YOUR_API_KEY"},
)
Supported operators
| Operator | Method | Example output |
|---|---|---|
| Plain term | Query("term") |
term |
| Exact phrase | .phrase("exact phrase") |
"exact phrase" |
| Force include | .include("term") |
+term |
| Exclude | .exclude("term") |
-term |
| File extension | .ext("pdf") |
ext:pdf |
| File type | .filetype("pdf") |
filetype:pdf |
| In title | .intitle("2023") |
intitle:2023 |
| In body | .inbody("keyword") |
inbody:keyword |
| In page | .inpage("keyword") |
inpage:keyword |
| Language (ISO 639-1) | .lang("es") |
lang:es |
| Language alias | .language("es") |
language:es |
| Location (ISO 3166-1) | .loc("gb") |
loc:gb |
| Location alias | .location("gb") |
location:gb |
| Site / domain | .site("example.com") |
site:example.com |
| Logical AND | .and_(other) or & |
term1 AND term2 |
| Logical OR | .or_(other) or | |
term1 OR term2 |
| Logical NOT | .not_(other) or ~ |
NOT term |
Operators can be placed anywhere in the query string, matching Brave Search behavior.
Examples
Official documentation examples
from bsqb import Query
# Academic research
Query("climate change").filetype("pdf").site("edu").intitle("2024").build()
# climate change filetype:pdf site:edu intitle:2024
# Multilingual content
Query("recettes cuisine").loc("ca").lang("fr").build()
# recettes cuisine loc:ca lang:fr
# Competitive analysis
(
Query("AI startup")
.exclude("google")
.exclude("microsoft")
.exclude("amazon")
.exclude("meta")
.build()
)
# AI startup -google -microsoft -amazon -meta
# Technical documentation
(
Query("python")
.phrase("asyncio")
.intitle("documentation")
.site("docs.python.org")
.build()
)
# python "asyncio" intitle:documentation site:docs.python.org
Logical operators
from bsqb import Query, combine_and, combine_or
# AND — visa info in English from UK sites
Query("visa").loc("gb").and_(Query().lang("en")).build()
# visa loc:gb AND lang:en
# OR — travel requirements for Australia or New Zealand
(
Query("travel requirements")
.inpage("australia")
.or_(Query().inpage("new zealand"))
.build()
)
# travel requirements inpage:australia OR inpage:"new zealand"
# NOT — exclude a domain
Query("brave search").not_(Query().site("brave.com")).build()
# brave search NOT site:brave.com
# Python operators
(Query("coffee") | Query("tea")).exclude("starbucks").build()
# coffee OR tea -starbucks
# Combine multiple queries
combine_and(Query("visa").loc("gb"), Query().lang("en")).build()
combine_or(Query().site("reuters.com"), Query().site("bloomberg.com")).build()
Advanced usage
from bsqb import Query, phrase, raw, term
# Build from AST nodes
Query.from_nodes(term("python"), phrase("asyncio"), raw("site:docs.python.org"))
# Wrap an existing query string
Query.parse("machine learning filetype:pdf lang:en").build()
# Skip validation for edge cases
Query.parse("...").build(validate=False)
Validation
The Brave Search API enforces these limits on the q parameter:
- Maximum 400 characters
- Maximum 50 words
- Query cannot be empty
Call .build() to validate (default), or .render() / str() to get the string without validation:
from bsqb import Query, QueryValidationError, EmptyQueryError
query = Query("hello world")
query.render() # "hello world" — no validation
query.build() # "hello world" — validates limits
try:
Query().build()
except EmptyQueryError:
...
try:
Query.parse(" ".join(["word"] * 51)).build()
except QueryValidationError as exc:
print(exc.query)
Integration with Brave Search API
Search operators are included in the q parameter. Set operators=true (the default) in API requests:
import urllib.parse
import urllib.request
from bsqb import Query
query = Query("python").phrase("asyncio").filetype("pdf").lang("en")
params = {
"q": query.build(),
"count": "10",
"operators": "true",
}
url = "https://api.search.brave.com/res/v1/web/search?" + urllib.parse.urlencode(params)
request = urllib.request.Request(
url,
headers={
"Accept": "application/json",
"X-Subscription-Token": "YOUR_API_KEY",
},
)
For POST requests with long queries, pass the built string as the q field in the request body.
Development
git clone https://github.com/kodzghly/bsqb.git
cd bsqb
python -m pip install -e ".[dev]"
# Run tests
pytest
# Lint and type check
ruff check src tests
mypy src
Publishing to PyPI
This package uses Hatchling as the build backend.
First-time setup
- Create accounts on PyPI and TestPyPI
- Enable 2FA on both accounts
- Create an API token at pypi.org/manage/account/token/
Test on TestPyPI
python -m pip install --upgrade build twine
# Bump version in pyproject.toml and src/bsqb/__init__.py
python -m build
# Upload to TestPyPI
twine upload --repository testpypi dist/*
# Verify installation
pip install --index-url https://test.pypi.org/simple/ bsqb
Publish to PyPI
python -m build
twine check dist/*
twine upload dist/*
Recommended: publish via GitHub Actions
Add trusted publishing on PyPI for your repository, then create a release workflow triggered by git tags:
# .github/workflows/publish.yml
name: Publish
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install build
- run: python -m build
- uses: pypa/gh-action-pypi-publish@release/v1
Create a GitHub release with tag v0.1.0 to trigger publication.
References
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bsqb-0.1.0.tar.gz.
File metadata
- Download URL: bsqb-0.1.0.tar.gz
- Upload date:
- Size: 9.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8feaaa9c6758972d3aa71ba4fba93c9ba12ccc4292d10f53b0ceb478b5ced650
|
|
| MD5 |
07c62c39c5883888b1e1e3396b920549
|
|
| BLAKE2b-256 |
fe0a8e78882f8ef0cf9acd03a1535c7acfa9871e8f77e50057faa4b0dd0656ed
|
Provenance
The following attestation bundles were made for bsqb-0.1.0.tar.gz:
Publisher:
publish.yml on KodzghlyCZ/bsqb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bsqb-0.1.0.tar.gz -
Subject digest:
8feaaa9c6758972d3aa71ba4fba93c9ba12ccc4292d10f53b0ceb478b5ced650 - Sigstore transparency entry: 1868803443
- Sigstore integration time:
-
Permalink:
KodzghlyCZ/bsqb@fa814808286ca1e885bf82e59419276ea902676d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/KodzghlyCZ
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fa814808286ca1e885bf82e59419276ea902676d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file bsqb-0.1.0-py3-none-any.whl.
File metadata
- Download URL: bsqb-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b3f8bb7b9d85300f7b86f9ecf71ea8ce1b53d8769edc4c161ccbe700d1a648f
|
|
| MD5 |
fd96157d2ce945cf2da05edd70c86cdc
|
|
| BLAKE2b-256 |
f152056bd4d7b0a8111d530ceea176ba9873d5a5d2e1ad9e07b454bbecb1d5a4
|
Provenance
The following attestation bundles were made for bsqb-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on KodzghlyCZ/bsqb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bsqb-0.1.0-py3-none-any.whl -
Subject digest:
5b3f8bb7b9d85300f7b86f9ecf71ea8ce1b53d8769edc4c161ccbe700d1a648f - Sigstore transparency entry: 1868803527
- Sigstore integration time:
-
Permalink:
KodzghlyCZ/bsqb@fa814808286ca1e885bf82e59419276ea902676d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/KodzghlyCZ
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fa814808286ca1e885bf82e59419276ea902676d -
Trigger Event:
workflow_dispatch
-
Statement type: