Skip to main content

vocab-bloom-hub

Typed Python client for the public read-only API of a Vocab Bloom Hub instance — an English dictionary with IPA, CEFR levels, sense-level definitions, examples, translations (Russian, Spanish, French, German, Portuguese, Chinese, Arabic) and inflected forms, served under /api/v1.

Documentation, the API reference and a playground: vocab-bloom-hub.com.

  • Sync (VocabBloomClient) and async (AsyncVocabBloomClient) on httpx; one method per endpoint.
  • pydantic models generated from the server's OpenAPI document — the types cannot drift from the API.
  • Typed exceptions, cursor iteration, optional ETag cache, words_dataframe() for notebooks.
  • Python 3.10+; dependencies: httpx, pydantic (pandas optional).

Install

pip install vocab-bloom-hub
# with pandas support
pip install "vocab-bloom-hub[pandas]"

pip skips prereleases by default (1.1.0b1, PEP 440 for 1.1.0-beta.1); add --pre to try one.

Quick start

from vocab_bloom_hub import NotFoundError, VocabBloomClient

client = VocabBloomClient("https://dict.example.com")

# search: relevance tiers, typo tolerance
result = client.search("definately")
print(result.meta.fuzzy, result.data[0].word)  # True definitely

# a headword with every part of speech, forms, meanings and translations
try:
    run = client.word("run")
    print(run.data[0].meanings[0].definition)
except NotFoundError:
    print("no such word")

# walk the whole dictionary, page after page
for word in client.iter_words(word_level=["A1", "A2"], with_meanings=True):
    print(word.word, len(word.meanings))

# a notebook: the filtered list as a DataFrame (pip install "vocab-bloom-hub[pandas]")
frame = client.words_dataframe(part_of_speech=["noun"], category=["IT"])

Async, the same methods awaited:

from vocab_bloom_hub import AsyncVocabBloomClient

async with AsyncVocabBloomClient("https://dict.example.com") as client:
    meta = await client.meta()
    async for word in client.iter_words(limit=100):
        ...

Standalone command-line example

The SDK includes a runnable lookup example without adding an installed command. It requires Python 3.10+ and uses only the standard library and the SDK dependencies. Install the SDK, then run this from the Python SDK directory:

python -m pip install vocab-bloom-hub
python examples/lookup_word.py --base-url https://dict.example.com run

See the complete lookup example. Its output includes dataset license and attribution details and marks entries modified on the instance.

API

Method Endpoint Answer
search(search, *, type, limit) GET /search SearchResponse
search_detailed(search, *, ...) GET /search/detailed DetailedSearchResponse
word(headword) GET /words/{word} HeadwordResponse
words_batch(words) POST /words/batch WordsBatchResponse — up to 50 headwords, one rate-limit unit; misses under meta.not_found
word_by_id(id) GET /words/id/{id} WordResponse
meanings(headword) GET /words/{word}/meanings MeaningsResponse
translations(headword, *, language) GET /words/{word}/translations TranslationsResponse
forms(headword) GET /words/{word}/forms FormsResponse
synonyms(headword) GET /words/{word}/synonyms LinksResponse — the linked headwords per meaning
antonyms(headword) GET /words/{word}/antonyms LinksResponse
history(headword) GET /words/{word}/history HistoryResponse — what was changed on the instance, with the values before and after
word_datasets(headword) GET /words/{word}/datasets WordDatasetsResponse — the headword in every dataset of the instance, a group per dataset with its terms
dataset_history(headword, dataset) GET /words/{word}/datasets/{dataset}/history HistoryResponse — the edits of the headword in one dataset
words(**filters, cursor, limit, with_...) GET /words WordsResponse (one page)
iter_words(**filters, ...) GET /words, following the cursor Iterator[Word]
iter_search_detailed(search, *, ...) GET /search/detailed, page after page Iterator[Word] — stops at the server's page cap (DETAILED_SEARCH_MAX_PAGE, 20)
random(**filters) GET /random WordResponse
meta() GET /meta MetaResponse
openapi() GET /openapi.json dict — the OpenAPI document
suggest(headword, ...) POST /suggestions SuggestionCreatedResponse — files a reader report (or an edit proposal) into the instance's moderation queue
words_dataframe(**filters, ...) GET /words, every page pandas.DataFrame (sync and async clients)

For a report from a dataset tab, pass its dataset name: client.suggest("run", dataset="reader_words", message="Please check this definition."). Omitting it uses the active dataset. Unknown, closed or deleted targets raise NotFoundError; the client-wide rate limit is shared across targets.

Every response is the { data, meta } envelope the API answers with, as a pydantic model. Filters (part_of_speech, word_level, language_register, category, area_variant, form_of_word) take lists of strings or of the exported enums (PartOfSpeech, WordLevel, ...); values of one filter are OR-ed, different filters are AND-ed. The contract itself — tiers, filters, cursor pagination, caching — is documented in the server's docs/api.md.

Options

VocabBloomClient(
    "https://dict.example.com",  # origin of the instance; /api/v1 is appended
    headers={"X-App": "my-app"},  # sent with every request
    timeout=10.0,  # seconds, or an httpx.Timeout
    cache=True,  # ETag revalidation (below); or your own ResponseCache
    retry={
        "attempts": 3,
        "backoff": 0.5,
        "max_delay": 60.0,
    },  # opt-in: retry the GET reads on 429 / 5xx (below)
    transport=...,  # a custom httpx transport (tests, instrumentation)
)

Use the client as a context manager (with / async with) to close the connection pool.

Errors

Exception When Fields
NotFoundError 404 status, code (word_doesnt_found), body
RateLimitError 429 — the public rate limit retry_after (seconds, from Retry-After)
NetworkError no answer: DNS, connection, TLS, timeout status == 0, code == "network_error"
VocabBloomError everything else status, code, body

code is the machine-readable error of the API (invalid_cursor, too_many_requests, ...), or http_error when the answer was not JSON (a proxy page, for instance).

Without the retry option the client never retries on its own: a RateLimitError carries retry_after (seconds) and backoff is the caller's decision.

Per-request options

Every method takes options= — a RequestOptions dict with headers (merged over the client's for that call) and timeout (seconds or an httpx.Timeout, replacing the client's) — the counterpart of the Node client's last argument:

client.word("run", options={"headers": {"X-Request-Id": "abc"}, "timeout": 2.0})

Every request carries User-Agent: vocab-bloom-hub-python/<version> (vocab_bloom_hub.USER_AGENT) so an operator can tell SDK traffic apart in the log; pass your own User-Agent in headers to replace it.

Retry

Off by default — the client documents exact request counts against the rate limit, so the loop is opt-in. With retry={} (or explicit attempts / backoff) a GET answered 429 or 5xx is sent again: after Retry-After when the server sent it, otherwise after backoff, then twice that, and so on, up to attempts tries in total (the first one included; 3 and 0.5 s by default); no single wait exceeds max_delay seconds (60 by default), whatever Retry-After says. POST requests (the batch lookup, a suggestion), 4xx answers and network errors are never retried.

ETag cache

With cache=True every GET answer is kept in memory per URL together with its ETag; the next read of the same URL sends If-None-Match and, on 304 Not Modified, returns the kept body — the round trip stays, the payload does not. MemoryCache holds 500 entries (least recently used out); pass any object with get(url) / set(url, entry) for a store of your own. Off by default.

Development

cd packages/python-sdk
uv sync                                           # Python 3.12 + dependencies into .venv
uv run python scripts/generate_models.py          # models from apps/server/openapi/public-v1.json
uv run python scripts/generate_models.py --check  # fail when the generated models are stale (CI)
uv run ruff check . && uv run ruff format --check . && uv run mypy
uv run pytest                                     # unit tests + the client against the real server

src/vocab_bloom_hub/_generated/models.py is produced by datamodel-code-generator from the committed public spec and committed itself: a contract change on the server shows up as a diff here, and tests/test_contract.py fails until every native /api/v1 operation of the spec has a client method. Compatibility adapters have generated models but no wrapper methods: they are intended for third-party clients replacing their base URL. The live tests start the server through yarn workspace server fixture:public-api (Node.js and the monorepo's dependencies installed), on an in-memory SQLite database.

License

MIT for the client. The dictionary data comes under the license of the dataset the instance serves — CC BY 4.0 for the project's own, the license of the source for another, the one its owner chose for a dataset of the instance's own: read license, attribution and license_text from meta() and show them with the data (title names the dataset). An entry the owner of the instance changed carries modified: true, and client.history(headword) says what was changed: say next to such an entry that it was changed. word_datasets(headword) answers from every dataset of the instance: each group carries the terms of its own dataset, and entries taken from several groups are bound by each of them.

A word may have several origins, contributions and licenses. Each license's origin_id refers to its source or contribution snapshot: preserve the name, version, links, attribution and notices, and respect license_relation (all or any). For example, OpenGloss words marked as derived from WordNet retain both sets of terms. An unchanged fork keeps the original terms; actual edits add the fork's captured contribution terms. The primary license from meta() does not replace these word-specific licenses. These fields are optional when reading older v1 servers. See the API source fields.

What the licenses ask for: DATA_LICENSE.md.

Search selected dictionaries

from vocab_bloom_hub import SearchDatasetResult

choices = client.datasets()  # public names and terms, installation order
flat = client.search("run", dataset="wiktionary")
detailed = client.search_detailed("run", dataset="wiktionary", with_meanings=True)
grouped = client.search_datasets("run", ["default", "wiktionary"], limit=5)
for group in grouped.data:
    if isinstance(group, SearchDatasetResult):
        print(group.dataset, group.data, group.meta.has_more)
    else:
        print(group.dataset, group.error)  # unavailable, not an empty match

The async client offers the same methods with await. iter_search_detailed also accepts dataset. Omit the selector for existing active-dictionary behavior. Unknown or closed targets raise NotFoundError, without fallback. Grouped search takes 1–5 names and charges one public budget unit per dataset. Ranking, IDs and pagination stay within each group; request just one name to advance it. Preserve source terms and the dataset in word-page links. Partial failures are not cached; selected reads and the inventory always revalidate.

Metadata

Release files for vocab-bloom-hub 1.3.0

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

Source distribution (sdist)

Source distribution for vocab-bloom-hub 1.3.0
File Size Uploaded Provenance Actions
vocab_bloom_hub-1.3.0.tar.gz 146.2 kB With provenance Details for vocab_bloom_hub-1.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for vocab-bloom-hub 1.3.0
File Interpreter ABI Platform Actions
vocab_bloom_hub-1.3.0-py3-none-any.whl Python 3 none any Details for vocab_bloom_hub-1.3.0-py3-none-any.whl

Total release size: 175.2 kB

Release files / vocab_bloom_hub-1.3.0.tar.gz

Download URL vocab_bloom_hub-1.3.0.tar.gz
Size 146.2 kB
Tags Source
SHA-256 checksum
How to use checksums
78f8f323940f13f690b958e33584cd10ec597fbe140366c401ec41f62e58a18d
BLAKE2b-256 checksum
How to use checksums
359679b15a7cd8c3fdff8a7a7c799fa8ab0b8cfe4233e9aff96c72ffaebf83e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / vocab_bloom_hub-1.3.0-py3-none-any.whl

Download URL vocab_bloom_hub-1.3.0-py3-none-any.whl
Size 29.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b337adba07b3e1b45f4f5d20e06898f766e563be93266a5296e4533d390f08e0
BLAKE2b-256 checksum
How to use checksums
3180eba93db1aadab8d4660ae13b28be06896105277bb9dc598ad11686bc4c9c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Security for vocab-bloom-hub 1.3.0

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 vocab_bloom_hub-1.3.0.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 Oct 20, 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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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