adspub
Machine-readable view of the Google Ad Manager API version list, with a CLI built for both humans and AI agents.
Google only publishes which Ad Manager (formerly DoubleClick for Publishers)
API versions are supported on a human-readable page:
https://ads.google.com/apis/ads/publisher. Versions rotate several times a
year, get marked deprecated, and then disappear. adspub turns that page into
JSON, caches it locally, and answers two questions reliably:
- Which version is the latest?
- Is the version I am pinned to still active, deprecated, or gone?
Status rules:
| Status | Meaning |
|---|---|
active |
Listed on the page without a deprecation marker. Safe to use. |
deprecated |
Listed with the "Deprecated" marker. Still callable, migrate soon. |
unlisted |
Not on the page at all. Assumed sunset and no longer callable. |
Install
uv tool install adspub # as a global CLI tool
uvx adspub latest # or run without installing
pip install adspub # or into a project
Supports Python 3.11 through 3.15.
Human usage
adspub latest # v202608
adspub list # table of all versions with status
adspub check v202602 # active, deprecated or unlisted
adspub check v202511 v202602 # several at once; worst status sets the exit code
adspub services v202608 # SOAP services and WSDL/doc links in a version
adspub dump -o catalog.json # full catalog as JSON
adspub cache info # where the cache is and how old
adspub cache clear
Global options go before the command:
adspub --json latest # JSON output, every command supports it
adspub --refresh list # ignore the cache and fetch now
adspub --offline check v202602 # never touch the network
adspub --max-age 3600 latest # reuse the cache for at most one hour
Example:
$ adspub check v202511 v202602 v202608 v202405
deprecated v202511 is deprecated and will be removed; migrate to v202608.
active v202602 is active but not the latest; v202608 is newer.
active v202608 is the latest active version.
unlisted v202405 is not listed and is assumed to be sunset; migrate to v202608.
$ echo $?
20
Caching
Results are cached as a single JSON file for 24 hours by default:
- Linux:
~/.cache/adspub/catalog.json - macOS:
~/Library/Caches/adspub/catalog.json - Windows:
%LOCALAPPDATA%\adspub\adspub\Cache\catalog.json
Override the location with --cache-dir or the ADSPUB_CACHE_DIR environment
variable. If the network is down and a cache exists, the stale cache is used
and a warning is printed to stderr. JSON output always reports where the data
came from (network, cache or stale-cache) and when it was fetched.
Use in CI
Fail a build when the pinned version is no longer active:
adspub check "$AD_MANAGER_API_VERSION"
Exit code 0 means active, 10 deprecated, 20 unlisted. Use
|| [ $? -eq 10 ] if you want deprecated to warn rather than fail.
AI agent usage
The CLI is designed so an agent can call it without parsing prose:
--jsongives structured output on every command.- Exit codes encode the answer:
0active,10deprecated,20unlisted,1runtime error (network or parse),2usage error. - Output includes a
metablock withsourceandfetched_atso the agent can decide whether to re-run with--refresh. adspub schemaprints the JSON Schema of the catalog document.adspub parse page.htmlparses a saved copy with no network or cache.
Paste this into your agent's instructions file (CLAUDE.md, AGENTS.md,
a system prompt, or a tool description):
## Google Ad Manager API versions
Use the `adspub` CLI (install: `uv tool install adspub`, or run `uvx adspub`)
to find out which Ad Manager API versions are valid. Never guess a version.
- Latest version: `adspub --json latest`
- Check a pinned version: `adspub --json check v202602`
- All versions: `adspub --json list`
- Force a fresh fetch: add `--refresh` before the command
Exit codes for `check`: 0 active, 10 deprecated, 20 unlisted (assume removed).
Read `.results[].status`, `.results[].latest` and `.results[].newer_versions`
from the JSON. If `.meta.source` is `stale-cache`, retry with `--refresh`.
Example JSON from adspub --json check v202511:
{
"ok": false,
"exit_code": 10,
"latest": "v202608",
"results": [
{
"version": "v202511",
"status": "deprecated",
"is_latest": false,
"latest": "v202608",
"newer_versions": ["v202602", "v202605", "v202608"],
"message": "v202511 is deprecated and will be removed; migrate to v202608."
}
],
"meta": {
"source": "cache",
"fetched_at": "2026-10-09T09:47:14.401329+00:00",
"cache_age_seconds": 0.7,
"source_url": "https://ads.google.com/apis/ads/publisher",
"warning": null
}
}
As a tool for a Claude agent
A minimal tool definition that wraps the CLI:
{
"name": "ad_manager_api_version_check",
"description": "Check whether a Google Ad Manager API version (vYYYYMM) is active, deprecated or unlisted, and get the latest version. Runs `adspub --json check <version>`.",
"input_schema": {
"type": "object",
"properties": {
"version": { "type": "string", "description": "Version such as v202602" }
},
"required": ["version"]
}
}
The tool handler runs adspub --json check <version> and returns stdout
verbatim.
Python API
Everything is async. The CLI is a thin layer over these functions.
import asyncio
from adspub import load_catalog
async def main() -> None:
result = await load_catalog() # cache-aware; fetches when stale
catalog = result.catalog
print(result.source) # network | cache | stale-cache
print(catalog.latest.version) # v202608
print([v.version for v in catalog.deprecated])
check = catalog.check("v202511")
print(check.status, check.newer_versions, check.message)
asyncio.run(main())
Other entry points:
from adspub import fetch_catalog, parse_catalog
catalog = await fetch_catalog() # no cache
catalog = parse_catalog(html, source_url="https://...") # from a string
load_catalog accepts refresh, offline, max_age (seconds), cache_dir,
allow_stale, timeout, and an optional httpx.AsyncClient for connection
reuse or testing with httpx.MockTransport.
All models are Pydantic v2 (Catalog, ApiVersion, Service,
VersionCheck), so model_dump_json() and model_json_schema() work as
expected.
Development
git clone https://github.com/eivl/adspub && cd adspub
uv sync # creates .venv with dev dependencies
uv run pre-commit install # ruff, mypy, uv lock check on commit
uv run pytest # tests
uv run pytest --cov # with coverage
uv run ruff check . && uv run ruff format . && uv run mypy
uv run --python 3.11 pytest # any supported interpreter
The demo GIF in this README is rendered from demo/demo.tape with
VHS: run vhs demo/demo.tape locally, or
trigger the "Render demo" workflow, which commits the updated GIF.
Tests never touch the network. The parser is tested against
tests/fixtures/publisher.html, a saved copy of the real page. A weekly GitHub
Actions job parses the live page and warns when the fixture falls behind.
Releasing
Releases publish to PyPI via GitHub Actions trusted publishing when a v* tag
is pushed:
uv version --bump minor
git commit -am "Release v$(uv version --short)"
git tag "v$(uv version --short)"
git push && git push --tags
One-time setup: on PyPI, add a trusted publisher for this repository with
workflow publish.yml and environment pypi, and create a pypi environment
in the GitHub repository settings.
License
MIT
Metadata
Release files for adspub 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| adspub-0.1.1.tar.gz | 15.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| adspub-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.8 kB
Release files / adspub-0.1.1.tar.gz
| Download URL | adspub-0.1.1.tar.gz |
|---|---|
| Size | 15.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
31197039dd19d7a8ba5853ab8cb428097c51fe12eb29c4486188a61956252d98
|
|
BLAKE2b-256 checksum How to use checksums |
d8d67cfad0f9b0c9cce206da591753cb84a548973868a9fcb4e127f431d36905
|
| 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 9, 2026.
Transparency logRelease files / adspub-0.1.1-py3-none-any.whl
| Download URL | adspub-0.1.1-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
06af81db93f047e42dcbb1d85a41d454622d930f8d5cf89d9bfd3b6dc710fbed
|
|
BLAKE2b-256 checksum How to use checksums |
13d35c5384efbd50ed329155b81dba471bd1c960804e2ab8dfdb8036c7d11536
|
| 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 9, 2026.
Transparency log