Skip to main content

mangools

Python client for the Mangools API: KWFinder, SERPChecker, SERPWatcher, LinkMiner, SiteProfiler and AI Search Watcher. httpx-based, fully typed, py.typed.

Generated client. Every module under mangools/ is generated from the live OpenAPI document at https://api.mangools.com/v3/openapi.json; only the MangoolsClient factory comes from a hand-written template. The 0.x series changes shape when the API does. See Known gaps.

Install

pip install mangools

Python 3.9 or newer. Runtime dependencies: httpx, attrs, python-dateutil.

Hello world

import os

from mangools import MangoolsClient
from mangools.api.aisearchwatcher import get_aiwatcher_monitors
from mangools.api.kwfinder import get_kwfinder_related_keywords
from mangools.models import Error

client = MangoolsClient(api_key=os.environ["MANGOOLS_API_KEY"])

related = get_kwfinder_related_keywords.sync(client=client, kw="seo tools")
if isinstance(related, Error):
    raise SystemExit(f"{related.error.type_}: {related.error.message}")
if related is not None and related.keywords:
    print(f"related keywords ({related.count_keywords_before_limit} before the limit):")
    for keyword in related.keywords[:5]:
        print(f"  {keyword.kw!r:<28} sv={keyword.sv} cpc={keyword.cpc} seo={keyword.seo}")

monitors = get_aiwatcher_monitors.sync(client=client)
if isinstance(monitors, Error):
    raise SystemExit(f"{monitors.error.type_}: {monitors.error.message}")
if monitors is not None and monitors.monitors:
    print(f"\n{len(monitors.monitors)} AI Search Watcher monitors:")
    for monitor in monitors.monitors:
        print(f"  {monitor.field_id}  {monitor.brand!r} -> {monitor.domain}")

get_kwfinder_related_keywords also takes location_id and language_id; both default to 0 (worldwide / all languages). Resolve location IDs with mangools.api.kwfinder.get_mangools_locations.

Authentication

Mangools authenticates with an API key in the x-access-token header. It is not an RFC 6750 bearer token, so Authorization: Bearer … will not work.

import os

from mangools import MangoolsClient

client = MangoolsClient(api_key=os.environ["MANGOOLS_API_KEY"])

MangoolsClient is a thin factory over the generated AuthenticatedClient that fills in the header name from the spec's ApiKeyAuth scheme. Keyword arguments are forwarded verbatim, so timeout=httpx.Timeout(30.0), headers={...}, follow_redirects=True, verify_ssl=False and httpx_args={...} all work:

import httpx

client = MangoolsClient(
    api_key=os.environ["MANGOOLS_API_KEY"],
    timeout=httpx.Timeout(30.0),
    raise_on_unexpected_status=True,
)

base_url defaults to the spec's servers[0].url, https://api.mangools.com/v3. Override it to point at another server, for example a local mock of the API.

Never hard-code the key. Read it from the environment or a secret manager.

Calling an endpoint

Every operation is a module under mangools.api.<tag> exposing four functions:

function returns on an undocumented status
sync(...) the parsed body, or None None (or raises if raise_on_unexpected_status)
sync_detailed(...) Response[T] with status_code, headers, content, parsed same
asyncio(...) awaitable parsed body same
asyncio_detailed(...) awaitable Response[T] same
import asyncio

from mangools.api.kwfinder import get_kwfinder_limits

async def main() -> None:
    response = await get_kwfinder_limits.asyncio_detailed(client=client)
    print(response.status_code, response.parsed)

asyncio.run(main())

Use sync_detailed / asyncio_detailed when you need the status code or headers — for example the Retry-After on a 429.

Optional fields are Unset, not None, because the API distinguishes "absent" from "explicitly null". Unset is falsy, and isinstance(value, Unset) narrows under mypy:

from mangools.types import Unset

if not isinstance(keyword.sv, Unset):
    ...       # keyword.sv is a float here

Errors

Most non-2xx responses parse into the Error envelope, so error handling is a type check rather than a status-code table:

from mangools.models import Error
from mangools.types import UNSET

result = get_kwfinder_related_keywords.sync(client=client, kw="seo tools")
if isinstance(result, Error):
    print(result.error.type_, result.error.message)
    if result.error.retry_after is not UNSET:
        print("retry after", result.error.retry_after, "s")

error.errors carries the validation messages on a 422 and error.retry_after the wait in seconds on a 429. A status the spec does not document raises mangools.errors.UnexpectedStatus when the client is built with raise_on_unexpected_status=True.

Two 429 responses exist and they do not look alike. The application's own 429 is the Error envelope above. The gateway's is a plain HTML page produced before the request reaches the application; the document declares it as text/html, and the operations that carry it gain a str arm rather than an Error arm:

from mangools.api.kwfinder import get_kwfinder_limits

limits = get_kwfinder_limits.sync(client=client)   # Union[Limit, str] | None
if isinstance(limits, str):
    ...       # the gateway HTML page: the request rate limit was exceeded

GET /kwfinder/limits accepts an anonymous caller and has no validation or quota gate, so there is no Error branch to check for there.

Typing

The package ships a PEP 561 py.typed marker, so mypy and pyright type-check your call sites with no stub package. The generated client itself passes mypy --strict with no type: ignore anywhere.

How the package is built

Nothing generated and no copy of the API definition is committed. Git holds the generator configuration, two templates and the build scripts; everything else is produced from the live document:

pip install -r requirements-dev.txt
python scripts/fetch_spec.py        # writes openapi.json from https://api.mangools.com/v3/openapi.json
VERSION=0.0.0.dev0 ./scripts/generate.sh

SPEC_URL overrides the address of the document, and VERSION sets the package version (default 0.0.0.dev0). A build that cannot download the document fails; there is no fallback copy. generate.sh also writes SPEC_GAPS.md and TYPE_GAPS.md, two reports of what the document does not describe yet.

The package is generated by openapi-python-client, pinned in requirements-dev.txt.

Versions and releases

The info.version of the document stays 3.0.0 when its content changes, so a release is decided by the sha256 of the document instead. Every published sdist contains the openapi.json it was built from, and the package exposes the same hash:

import mangools

mangools.SPEC_SHA256   # sha256 of the document this version was generated from

A scheduled workflow compares the live document with the one inside the latest sdist on PyPI. When they differ, oasdiff decides the next version: a breaking change raises the minor version, anything else raises the patch version. The workflow then generates the package, runs every check, publishes through PyPI trusted publishing and creates a GitHub Release with the oasdiff changelog. Release notes live in GitHub Releases.

To see the decision without releasing anything:

OASDIFF=/path/to/oasdiff python scripts/release_decision.py --dry-run

Known gaps

SPEC_GAPS.md audits the live document and TYPE_GAPS.md lists every place where a type in this client stayed Any because the document does not describe the value. Both are written by generate.sh. CI fails when a response or model attribute is untyped for that reason, so a published version has none.

Optional fields are Unset, not None, and an optional group of fields that the API leaves out of a response stays Unset. Compare with UNSET, not with None.

This repository is maintained by the Mangools team and does not accept external pull requests or issues. Please send questions and bug reports to support@mangools.com.

License

Apache-2.0. See LICENSE.

Metadata

Release files for mangools 0.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 mangools 0.3.0
File Size Uploaded
mangools-0.3.0.tar.gz 209.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mangools 0.3.0
File Interpreter ABI Platform
mangools-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 624.1 kB

Release files / mangools-0.3.0.tar.gz

Download URL mangools-0.3.0.tar.gz
Size 209.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ba7edab8e0ac4bc377c317fdd55f19a1d81feab2b5d07b436f2ffec41580654c
BLAKE2b-256 checksum
How to use checksums
031f51c396c3d5dac236fcf168fb5e31f7209d0f72b1c11094bd638d612c7b74
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release files / mangools-0.3.0-py3-none-any.whl

Download URL mangools-0.3.0-py3-none-any.whl
Size 414.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c3bede2e58f479be63650c74d1b733449f88b22c7d5dd0d7b52a3a5b10ac520
BLAKE2b-256 checksum
How to use checksums
e7b812663c2863f3d58a8cc7da95ffb81825eb39c678f231bb9851f66476450c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.2

2 release files

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.0

2 release 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