Python SDK
pip install synter — typed client for 25 catalog tools plus an execute() escape hatch for any whitelisted PPC script.
Install
pip install synterQuickstart
import os
from synter import Synter
client = Synter(api_key=os.environ["SYNTER_API_KEY"]) # api_key is required
campaigns = client.campaigns.list(platform="google", status="ENABLED")
perf = client.analytics.get_performance() # default: google, LAST_7_DAYS
spend = client.analytics.get_daily_spend() # default: google, last 7 daysOptional analytics args
get_performance and get_daily_spend accept an optional platform (default google). Empty calls are valid. They dispatch to the platform pull scripts (pull_google_ads, pull_meta_ads, …), not to get_account_daily_spend.The client sends Authorization: Bearer syn_….
| Surface | Accepted | Use |
|---|---|---|
| REST tools endpoint | Authorization: Bearer syn_…, X-Synter-Key: syn_…, X-API-Key: syn_… | Authorization: Bearer syn_… |
| REST resource endpoints | Authorization: Bearer syn_…, X-Synter-Key: syn_… | Authorization: Bearer syn_… |
| SDKs (Python, TypeScript, Rust, Java, Go) and the synter CLI | The SDK sets the header for you | Pass the key to the client. Every SDK sends Authorization: Bearer syn_… |
| Local stdio MCP (npm @synterai/mcp-server) | Set SYNTER_API_KEY in the server's env block | The package sends Authorization: Bearer syn_… |
| Hosted MCP | Browser OAuth (the client sends Authorization: Bearer <OAuth access token>), or X-Synter-Key: syn_… for headless clients | OAuth. Headless: X-Synter-Key. Do not put an API key in Authorization on this host. |
Keys start with syn_ (sandbox keys with syn_test_). Create one at synterai.com/developer, keep it in SYNTER_API_KEY or a secret store, and send one header per request. On the hosted MCP endpoint, the Authorization header is reserved for the OAuth token the client gets from browser sign-in, so a failed key there shows up as a sign-in prompt rather than a key error. Use X-Synter-Key when you connect a hosted MCP client with a key. Details: Authentication.
Write: pause a campaign and change its daily budget
Both calls change a live Google Ads campaign. platform is required, but the request always goes to Google Ads; use the REST endpoints for other platforms.
import os
from synter import Synter
from synter.errors import SynterError
client = Synter(api_key=os.environ["SYNTER_API_KEY"])
paused = client.campaigns.pause(campaign_id="1234567890", platform="google")
try:
result = client.campaigns.update_budget(
campaign_id="1234567890", platform="google", daily_budget=75.0
)
except SynterError as err: # 403 = blocked by policy or missing scope
print(err.status, err.code)
raise
if result.get("status") == "pending_review":
print("Held for approval, nothing ran:", result["audit_id"])Writes the safety gate holds or refuses
With the default workspace policy, pausing a campaign and changing a budget run straight away and are recorded in the audit log. Enabling or resuming a campaign, deleting or removing anything, sending creative to a platform, and creating new campaigns can be held for approval. A held write returns HTTP 202 with status: "pending_review", an audit_id and the exact reviewed_args. Nothing has run at that point. The SDKs return this body as a normal result, so check status before you report success.
A write the policy refuses (blocked tool, outside operating hours, over a monetary limit) returns HTTP 403 with status: "blocked", and the SDKs raise their error type. Approval cannot override a block.
An API key cannot approve its own pending write. See Approval model and safety controls for who approves what and where.
Closed-Loop Readback Verification
For autonomous coding agents operating under authorized apply mode, synter >= 0.2.0provides atomic write-and-verify helpers. These mutate the campaign and immediately read back effective provider state rather than assuming success from a 2xx response:
# Atomic budget update with verification readback
budget_verified = client.campaigns.update_budget_and_verify(
campaign_id="1234567890",
daily_budget=25.0,
platform="google",
)
print("Effective provider budget:", budget_verified["verified_state"])
# Atomic pause with verification readback
pause_verified = client.campaigns.pause_and_verify(
campaign_id="1234567890",
platform="google",
)
print("Effective provider status:", pause_verified["verified_state"])Async
import asyncio
from synter import AsyncSynter
async def main():
async with AsyncSynter(api_key="syn_...") as client:
print(await client.campaigns.list(platform="google"))
asyncio.run(main())Any script
Use execute(script_name, args) for a whitelisted PPC script. MCP tool names such as list_campaigns are not scripts — call the typed method, or GET https://api.synterai.com/v1/campaigns, instead.
client.execute(
"google_ads_get_campaigns",
{"status": "ENABLED"},
platform="google",
)Full hosted MCP tools: /mcp/tools. Claude Code plugin: /guides/claude-plugin.
