synter (Python)
Python SDK for the Synter cross-platform advertising API — manage Google, Meta, LinkedIn, Microsoft, Reddit, TikTok, and X ad campaigns from one client.
Not yet published to PyPI. There is no
pip install synterfrom the index yet — install from source (see below) until Joel gives the go-ahead to publish. This is alpha software (0.1.0); expect breaking changes before a stable1.0.
This SDK is one of several parallel language implementations (TypeScript, Go, Java, Rust, plus a
Go CLI) that all implement the same contract described in
packages/sdk-shared/SPEC.md and
packages/sdk-shared/catalog.json in this monorepo. It talks
directly to https://syntermedia.ai/api/v1/tools/run — the same production endpoint the
published @synterai/mcp-server npm package uses internally, so every call here has already been
exercised in production by every MCP client (Claude, Cursor, Codex, ChatGPT).
Status
- Sync client (
Synter): fully implemented, all 25 tools. - Async client (
AsyncSynter): fully implemented, mirrors the sync client's entire surface. - Not published to PyPI. Built, linted, type-checked, and tested only.
Installation (from source)
git clone https://github.com/jshorwitz/synter-media.git
cd synter-media/packages/sdk-python
pip install -e .
Requires Python 3.9+.
Getting an API key
Create a key at syntermedia.ai/developer. Keys look like
syn_ followed by 32 base64url characters. Treat it like a password — anyone with it can act on
your connected ad accounts.
Quickstart
from synter import Synter
client = Synter(api_key="syn_...") # or read from an env var yourself
# List campaigns (defaults to Google if no platform is given)
campaigns = client.campaigns.list(platform="google", status="ENABLED", limit=20)
# Create a Search campaign
result = client.campaigns.create_search(
campaign_name="Q4 Launch",
daily_budget=50,
keywords=["running shoes", "trail shoes"],
headlines=["Buy Shoes Now", "Best Shoes 2026", "Free Shipping"],
descriptions=["Great shoes for less.", "Shop today."],
final_url="https://example.com/shoes",
)
# Pull performance metrics
perf = client.analytics.get_performance(platform="google", date_range="LAST_7_DAYS")
Async usage
import asyncio
from synter import AsyncSynter
async def main():
async with AsyncSynter(api_key="syn_...") as client:
campaigns = await client.campaigns.list(platform="google")
print(campaigns)
asyncio.run(main())
The escape hatch: execute()
Every backend script (140+ across 19 platforms) is reachable even without a typed method, via
execute() — the SDK-level mirror of the run_tool MCP tool:
# Idiomatic dict form (recommended): converted to CLI flags automatically.
client.execute("google_ads_list_audiences", {"account_id": "123", "status": "ENABLED"})
# Raw CLI-flag-array form, for full parity with the underlying backend contract.
client.execute("google_ads_list_audiences", ["--account-id", "123", "--status", "ENABLED"])
Error handling
Every failure raises synter.SynterError (or its subclass synter.SynterValidationError for
client-side validation failures caught before any network call):
from synter import Synter, SynterError, SynterValidationError
client = Synter(api_key="syn_...")
try:
client.campaigns.create_search(campaign_name="", daily_budget=50, ...)
except SynterValidationError as e:
print("You called it wrong:", e.message)
except SynterError as e:
print(f"API error [{e.status}] {e.message} (code={e.code})")
SynterError carries message, status (HTTP status, or 0 for network/timeout failures),
code, and details.
Retries
Requests are retried up to 3 times with exponential backoff (1s base, 10s cap), only for HTTP 429
(honoring the Retry-After header) and network/timeout errors. Other 4xx responses are never
retried. Default request timeout is 30 seconds; override with Synter(api_key=..., timeout=60.0).
Known issues (preserved, not silently fixed)
This SDK calls the real backend exactly as it behaves in production today, including three known
quirks documented in packages/sdk-shared/SPEC.md:
campaigns.pause()andcampaigns.update_budget()accept aplatformargument (schema parity) but always dispatch withplatform=googleregardless of what you pass.upload_image()accepts an optionalplatformargument but always dispatches withplatform=google.campaigns.create_display()useslandscape_image_url/square_image_url(backend flags--landscape-image/--square-image), whilecampaigns.create_pmax()uses the same-named Python parameters but different backend flags (--landscape-image-url/--square-image-url). The Python parameter names are consistent; the wire flags genuinely differ because the two backend scripts differ.
If these get fixed upstream, catalog.json will be updated first and every language SDK patched
together — this SDK will not "helpfully" diverge on its own.
Resource namespaces
| Namespace | Tools |
|---|---|
client.campaigns |
list, create_search, create_display, create_pmax, pause, update_budget |
client.analytics |
get_performance, get_daily_spend |
client.keywords |
add, add_negative |
client.conversions |
create, list, diagnose_tracking |
client.creative |
generate_image, generate_video |
client.meta |
create_campaign |
client.linkedin |
create_campaign |
client.reddit |
create_campaign |
client.audiences |
stage_artifact, sync, manage |
client.* (top level) |
list_ad_accounts, upload_image, list_landing_pages, execute |
Development
pip install -e ".[dev]"
pytest
mypy synter
ruff check synter tests
No test hits the real network or requires a SYNTER_API_KEY — HTTP is mocked with
respx.
Publishing
Nothing in this package is published to PyPI without explicit sign-off. Build/lint/test only.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file synter-0.1.0.tar.gz.
File metadata
- Download URL: synter-0.1.0.tar.gz
- Upload date:
- Size: 25.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
897f5c9f414a5b5df71be52e754e8e3498924d698c019961d766ab1fb3db64ad
|
|
| MD5 |
2a7e420c1ae20b9a6b9feb0d17409539
|
|
| BLAKE2b-256 |
785f112650a4999511ce257936ce749bf06ce96f52063c339d70b91074611a6a
|
File details
Details for the file synter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: synter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c05446daaae489c7da30f1d8f7c979c2301afcd46d7733abef594c2dc07d66ee
|
|
| MD5 |
982f3ea4cb614eb088167b2f92066fd2
|
|
| BLAKE2b-256 |
62271d9ce90a7cfe11f837f7680159f826af18efd57886611c5510b99a643f0e
|