Skip to main content

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 synter from 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 stable 1.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:

  1. campaigns.pause() and campaigns.update_budget() accept a platform argument (schema parity) but always dispatch with platform=google regardless of what you pass.
  2. upload_image() accepts an optional platform argument but always dispatches with platform=google.
  3. campaigns.create_display() uses landscape_image_url/square_image_url (backend flags --landscape-image/--square-image), while campaigns.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

synter-0.1.0.tar.gz (25.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

synter-0.1.0-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

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

Hashes for synter-0.1.0.tar.gz
Algorithm Hash digest
SHA256 897f5c9f414a5b5df71be52e754e8e3498924d698c019961d766ab1fb3db64ad
MD5 2a7e420c1ae20b9a6b9feb0d17409539
BLAKE2b-256 785f112650a4999511ce257936ce749bf06ce96f52063c339d70b91074611a6a

See more details on using hashes here.

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

Hashes for synter-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c05446daaae489c7da30f1d8f7c979c2301afcd46d7733abef594c2dc07d66ee
MD5 982f3ea4cb614eb088167b2f92066fd2
BLAKE2b-256 62271d9ce90a7cfe11f837f7680159f826af18efd57886611c5510b99a643f0e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page