You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(decisioning): a Google Ad Manager upstream helper (credentials, cached client, pinned API version) #1786
Every seller that sells Google Ad Manager inventory through the SDK writes the same ~150 lines before its first GAM call:
build googleads credentials from either an OAuth refresh token (oauth2.GoogleRefreshTokenClient) or a service-account key (google.oauth2.service_account.Credentials with the https://www.googleapis.com/auth/dfp scope, wrapped in oauth2.GoogleCredentialsClient);
create one AdManagerClient per network, cache it, and give it a SOAP timeout;
pin the Ad Manager API version in one place, so a googleads upgrade does not silently move every service to a new version;
route sandbox and mock accounts to a mock upstream instead of Google.
The SDK already owns the last part: PlatformHandler.upstream_url, upstream_for and adcp.decisioning.upstream route a sandbox account to get_mock_upstream_url, and upstream_url's docstring names GAM as an example. But the GAM client itself is left to each seller, so the mock routing and the real client are wired by hand.
Example
The Scope3 embedded sales agent's copy:
src/adapters/gam/auth.py (GAMAuthManager: OAuth or service account, from a key file or inline JSON);
src/adapters/gam/client.py (GAMClientManager: cached AdManagerClient per network, SOAP timeout, get_service(name, timeout_seconds=...), health check, reset_client());
src/adapters/gam/api_version.py (GAM_API_VERSION = "v202608", with an architecture test that no GAM client passes its own version).
The prebid salesagent this was forked from carries the same code.
Suggested shape
An optional extra (adcp[gam]) with, for example:
fromadcp.decisioning.upstream.gamimportGamCredentials, gam_clientclient=gam_client(
network_code="123",
credentials=GamCredentials.service_account(key_json) |GamCredentials.oauth(client_id, client_secret, refresh_token),
api_version="v202608", # required; one place to move itsoap_timeout_seconds=60,
)
service=client.service("OrderService")
It would be cached per (network, credentials), honor upstream_for / the mock upstream URL for sandbox accounts, and expose reset() for credential rotation. Optional and low priority: it removes duplicated code across GAM sellers rather than unblocking anything.
Closing as out of scope for the SDK. Thanks for writing it up.
The SDK stays protocol-level and vendor-neutral. An adcp[gam] extra would make one ad server's client (and its googleads / API-version churn) part of the SDK's surface and release cycle, and invite the same for every other ad server.
The vendor-neutral half is already here: PlatformHandler.upstream_url, upstream_for and adcp.decisioning.upstream route sandbox and mock accounts to the mock upstream. A seller's GAM client should plug into that.
The GAM-specific part (credentials, a cached AdManagerClient per network, the pinned API version) fits best in the seller (ESA's src/adapters/gam/). If several GAM sellers want to share one copy, a small standalone package such as adcp-gam would do that without tying the SDK to GAM.
Activity
bokelley commentedon Nov 2, 2026
Closing as out of scope for the SDK. Thanks for writing it up.
The SDK stays protocol-level and vendor-neutral. An
adcp[gam]extra would make one ad server's client (and itsgoogleads/ API-version churn) part of the SDK's surface and release cycle, and invite the same for every other ad server.The vendor-neutral half is already here:
PlatformHandler.upstream_url,upstream_forandadcp.decisioning.upstreamroute sandbox and mock accounts to the mock upstream. A seller's GAM client should plug into that.The GAM-specific part (credentials, a cached
AdManagerClientper network, the pinned API version) fits best in the seller (ESA'ssrc/adapters/gam/). If several GAM sellers want to share one copy, a small standalone package such asadcp-gamwould do that without tying the SDK to GAM.