Skip to main content

Synup SDK

Build local presence automations in minutes — review auto-responders, listings monitors, reputation dashboards, bulk onboarding tools, and more.

Installation

pip install synup

Quick start: auto-reply to reviews in 10 lines

import synup

client = synup.Synup()  # reads SYNUP_API_KEY from env

# Auto-reply to all 4-5 star reviews that haven't been answered
results = client.workflows.auto_reply_to_reviews(
    16808,
    template="Thanks for the {rating}-star review! We appreciate your support.",
    min_rating=4,
)
print(f"Replied to {len(results)} reviews")

Quick start: listings health audit in 3 lines

import synup

client = synup.Synup()

audit = client.workflows.listings_health_audit(16808)
print(f"Health score: {audit.health_score}%")
print(f"Synced: {audit.synced_count}, Issues: {audit.issue_count}")
print(f"Duplicates found: {len(audit.duplicates)}")

Configuration

import synup

# Minimal — reads SYNUP_API_KEY env var
client = synup.Synup()

# Explicit key
client = synup.Synup(api_key="your-api-key")

# With custom config
client = synup.Synup(
    api_key="your-api-key",
    timeout=300.0,       # request timeout in seconds (default: 240)
    max_retries=3,       # auto-retry on 429/5xx (default: 2)
)

Locations

# List with pagination
page = client.locations.list(first=10)
for loc in page:
    print(loc.name, loc.city, loc.stateIso)

# Next page
if page.has_more:
    next_page = page.next_page()

# Auto-paginate all
for loc in client.locations.list(first=100).auto_paging_iter():
    print(loc.name)

# Retrieve single location
location = client.locations.retrieve(16808)
print(location.name)

# Search
page = client.locations.search("cafe", first=20)

# By IDs, store codes, folder, or tags
locations = client.locations.list_by_ids([16808, 16749])
locations = client.locations.list_by_store_codes(["STORE01", "STORE02"])
locations = client.locations.list_by_folder(folder_name="franchise")
page = client.locations.list_by_tags(["vip", "recent"], first=20)

# Create
result = client.locations.create({
    "name": "Acme Inc",
    "storeId": "ACME01",
    "street": "123 Main St",
    "city": "New York",
    "stateIso": "NY",
    "postalCode": "10001",
    "countryIso": "US",
    "phone": "5551234567",
})
print(result.success, result.errors)

# Update, archive, activate
client.locations.update({"id": 16808, "phone": "5559876543"})
client.locations.archive([16808])
client.locations.activate([16808])

Reviews

# List reviews with filters
page = client.reviews.list(16808, first=20, rating_filters=[1, 2])
for review in page:
    print(review.content, review.rating)

# Auto-paginate all reviews
for review in client.reviews.list(16808, first=50).auto_paging_iter():
    print(review.content)

# Respond to a review
client.reviews.respond(interaction_id="uuid-here", content="Thank you!")

# Edit or archive a response
client.reviews.edit_response(review_id="...", response_id="...", content="Updated reply")
client.reviews.archive_response(response_id="...")

# Review analytics
overview = client.reviews.analytics.overview(16808, start_date="2024-01-01")
timeline = client.reviews.analytics.timeline(16808)
sites = client.reviews.analytics.sites_stats(16808)

# Review phrases
phrases = client.reviews.phrases(["TG9jYXRpb246MTY4MDg="], start_date="2024-01-01")

Listings

# Premium, voice, AI, and additional listings
premium = client.listings.premium(16808)
voice = client.listings.voice(16808)
ai = client.listings.ai(16808)
additional = client.listings.additional(16808)

# Duplicates
dupes = client.listings.duplicates(16808)
client.listings.mark_duplicate(16808, ["listing-item-id"])
client.listings.mark_not_duplicate(16808, ["listing-item-id"])

# Connect/disconnect
client.listings.connect(16808, "listing-id", "account-id")
client.listings.disconnect(16808, "GOOGLE")

Analytics

google = client.analytics.google(16808, from_date="2024-01-01")
bing = client.analytics.bing(16808)
facebook = client.analytics.facebook(16808)

# Rankings
timeline = client.analytics.rankings_timeline(
    [16808], "2024-01-01", "2024-03-31", ["Google"]
)
histogram = client.analytics.rankings_histogram(
    [16808], "2024-01-01", "2024-03-31", ["Google"]
)

Keywords

keywords = client.keywords.list(16808)
performance = client.keywords.performance(16808, from_date="2024-01-01")
created = client.keywords.add(16808, ["plumber", "plumbing near me"])
client.keywords.archive("keyword-id")

Campaigns

campaigns = client.campaigns.list(16808)
result = client.campaigns.create(
    16808,
    name="Holiday Feedback",
    customers=[{"name": "John", "email": "john@example.com"}],
    screening=False,
)
client.campaigns.add_customers("campaign-id", [{"name": "Jane", "email": "jane@example.com"}])

Folders

folders = client.folders.list()
tree = client.folders.tree()
client.folders.create("franchise", parent_folder_name="all_franchise")
client.folders.rename("Old Name", "New Name")
client.folders.add_locations("franchise", [16808, 16749])
client.folders.remove_locations([16808])
client.folders.delete("folder-name")

Users

users = client.users.list()
roles = client.users.roles()
result = client.users.create(email="jane@example.com", role_id="...", first_name="Jane")
client.users.update("user-id", first_name="Jane Updated")
client.users.add_locations("user-id", [16808])
client.users.remove_locations("user-id", [16808])

Tags

tags = client.tags.list()
client.tags.add(16808, "vip")
client.tags.remove(16808, "old-tag")

Photos

photos = client.photos.list(16808)
client.photos.add(16808, [{"photo": "https://example.com/img.jpg", "type": "ADDITIONAL"}])
client.photos.remove(16808, ["photo-id"])
client.photos.star(16808, ["media-id"], starred=True)

Connected accounts

accounts = client.connected_accounts.list()
url = client.connected_accounts.connect_google("https://ok.com", "https://err.com")
client.connected_accounts.disconnect_google("account-id")

# OAuth for single location
url = client.connected_accounts.oauth_url(16808, "GOOGLE", "https://ok.com", "https://err.com")
client.connected_accounts.oauth_disconnect(16808, "FACEBOOK")

Grid reports

result = client.grid_reports.create(
    location_id=16808,
    keywords=["italian restaurant"],
    business_name="Chianti",
    business_street="No 12, 5th Block",
    business_city="Bengaluru",
    business_state="Karnataka",
    business_country="India",
    latitude=12.935216,
    longitude=77.619961,
    distance=20,
    distance_unit="km",
    grid_size=3,
)
report = client.grid_reports.retrieve("report-id")
reports = client.grid_reports.list(16808, page_size=20, page=1)

Automations

result = client.automations.temporary_close(
    name="Holiday closure",
    start_date="2025-12-24",
    start_time="18:00:00",
    end_date="2025-12-26",
    location_id=16808,
)

Account-level

sites = client.plan_sites()
countries = client.countries()
subs = client.subscriptions()

Workflows — pre-built automations

High-level functions that combine multiple API calls into real product features.

# Auto-reply to positive reviews
results = client.workflows.auto_reply_to_reviews(
    16808,
    template="Thanks for the {rating}-star review!",
    min_rating=4,
    dry_run=True,  # preview without sending
)

# Onboard a location with folder, tags, and keywords in one call
result = client.workflows.onboard_location(
    name="Acme Coffee",
    street="123 Main St",
    city="New York",
    state="NY",
    postal_code="10001",
    country="US",
    phone="5551234567",
    folder_name="NYC Stores",
    tags=["new", "coffee"],
    keywords=["coffee shop near me"],
)

# Bulk onboard from CSV
results = client.workflows.bulk_onboard_locations("locations.csv", folder_name="Imported")

# Weekly reputation report
report = client.workflows.weekly_reputation_report(16808)
print(report.review_summary, report.analytics, report.listings_health)

# Listings health audit
audit = client.workflows.listings_health_audit(16808)
print(f"Score: {audit.health_score}%, Issues: {audit.issue_count}")

Error handling

import synup

client = synup.Synup()

try:
    client.locations.list()
except synup.AuthenticationError:
    print("Invalid API key")
except synup.RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")
except synup.NotFoundError:
    print("Resource not found")
except synup.ValidationError as e:
    print(f"Bad request: {e.response_body}")
except synup.APIError as e:
    print(f"API error {e.status_code}: {e.response_body}")
except synup.APIConnectionError:
    print("Network error — could not reach API")

Version

Current version: 0.4.2

Metadata

Release files for synup 0.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for synup 0.4.2
File Size Uploaded Provenance
synup-0.4.2.tar.gz 31.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for synup 0.4.2
File Interpreter ABI Platform
synup-0.4.2-py3-none-any.whl Python 3 none any Details

Total release size: 63.2 kB

Release files / synup-0.4.2.tar.gz

Download URL synup-0.4.2.tar.gz
Size 31.9 kB
Tags Source
SHA-256 checksum
How to use checksums
362141a13fb9c43a29c44267f8bac2fc8e975b6d371b3c799f43b60da2eb0ecc
BLAKE2b-256 checksum
How to use checksums
4c28a53c045e1a251539848c4ef178ba80db6f0788dd8292000d82b54c3d6e26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.7

Release files / synup-0.4.2-py3-none-any.whl

Download URL synup-0.4.2-py3-none-any.whl
Size 31.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
05df6b38b899407e53420b253c0ef99bd44b858c18753c23714c6ecbc864a6e3
BLAKE2b-256 checksum
How to use checksums
9bd55153b36ad198cb82bd13f75855edcef00680049eb0358343c6d9ebd3b38b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.7

Security for synup 0.4.2

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page