Quickstart
Connect to Listings API, list your locations, create one in a single call, and respond to a review.
1. Set your API key
Get your key from API Keys → New key in the Listings API developer dashboard. The SDK reads it from the LISTINGSAPI_KEY environment variable
automatically.
export LISTINGSAPI_KEY="your-api-key"2. List your locations
import listingsapi client = listingsapi.ListingsAPI() page = client.locations.list(first=5)for loc in page: print(loc.name, "--", loc.city, loc.stateIso) print(f"\nShowing {len(page)} locations, has_more={page.has_more}")python quickstart.pyExpected output:
Acme Dental Downtown -- New York NYAcme Dental Midtown -- New York NYAcme Dental Brooklyn -- Brooklyn NY Showing 3 locations, has_more=FalseTo request every page automatically, use
client.locations.list(first=100).auto_paging_iter(); it follows cursor
pages so you never manage cursors by hand.
3. Create a location in one call
locations.add() takes every mandatory field as a keyword argument and
validates them client-side (name length, 200-character description minimum,
category, country, city) before any network call, so a bad payload fails fast
with a clear ValidationError.
import listingsapi client = listingsapi.ListingsAPI() result = client.locations.add( name="Acme Dental Uptown", description=( "Acme Dental Uptown is a family-owned dental practice on the Upper " "West Side of New York offering preventive care, cosmetic dentistry, " "orthodontics, and emergency appointments. Our board-certified team " "combines modern equipment with a gentle, patient-first approach." ), sub_category_id=1432, country_iso="US", city="New York", street="482 Columbus Ave", state_iso="NY", postal_code="10024", phone="6443859313",) print(result.location.id)print(result.location.name)Publishers use the description as the primary listing copy, which is why the
API requires at least 200 characters. Get valid sub_category_id values from
client.subcategories().
The new ID comes back as a base64 string like TG9jYXRpb246MTY4MDg=.
Numeric and base64 IDs are interchangeable everywhere the SDK takes a
location ID, so client.locations.retrieve(16808) and
client.locations.retrieve("TG9jYXRpb246MTY4MDg=") fetch the same location.
4. Respond to a review
List reviews for a location, then reply using the review's interactionId:
import listingsapi client = listingsapi.ListingsAPI() reviews = client.reviews.list(16808, first=10, rating_filters=[4, 5])for review in reviews: print(review.rating, "-", review.get("content")) # A page can come back empty, and not every review is repliable: some sites# allow one owner reply per review, and some are read-only for the account.repliable = [r for r in reviews if r.get("canRespond")]if not repliable: print("nothing to reply to")else: client.reviews.respond( interaction_id=repliable[0].interactionId, content="Thank you for the kind review! We look forward to seeing you again soon.", )Check canRespond before replying, and never index a page without checking it
is non-empty first: reviews[0] on an empty page raises IndexError, and
replying to a review the source platform has closed fails at the publisher
rather than at the API.
If the platform rejects the reply, the SDK raises ValidationError even when
the HTTP status is 200; you never check success flags yourself.
5. Handle errors
import listingsapi try: client = listingsapi.ListingsAPI(api_key="bad-key") client.locations.list()except listingsapi.AuthenticationError as e: print("Invalid key:", e.code)Invalid keys come back as HTTP 401 with the platform code SY90005; the SDK
raises AuthenticationError. It also defensively handles the same error code
if it arrives inside a 200 body. See
Error handling for the full exception hierarchy
and the error codes reference for the HTTP status matrix.
Next steps
- Configuration: custom timeouts and retries
- Use cases: end-to-end recipes
- Locations resource: create, update, search