Locations
Create, read, update, and manage the archive lifecycle of the locations belonging to your clients.
Returns a page of locations, newest first. All filters are optional and combine together (a location must match all of them); to page through more results, pass the returned nextCursor back in.
List / search locations
/api/v1/locations{
"data": [
{
"id": "loc_456",
"name": "Acme Dental — Downtown",
"city": "Austin",
"state": "TX",
"clientId": "cli_123",
"createdAt": "2026-01-15T10:05:00.000Z"
}
]
}Creates a new location for an existing client and submits it for publishing.
Create a location
/api/v1/locations{
"data": {
"locationId": "loc_456"
}
}Returns the full business profile for one location: address, hours, categories, attributes, services, media, and verification status.
Get a location
/api/v1/locations/{id}{
"data": {
"id": "loc_456",
"name": "Acme Dental — Downtown",
"description": null,
"tagline": null,
"storeCode": null,
"logoUrl": null,
"street": "123 Main St",
"street1": null,
"city": "Austin",
"state": "TX",
"postalCode": "78701",
"country": "US",
"latitude": 30.2672,
"longitude": -97.7431,
"phone": "+15125551234",
"additionalPhones": [],
"website": "https://acmedental.com",
"businessEmail": null,
"categoryName": "Dentist",
"primaryCategoryDisplay": "Dentist",
"primaryCategoryGoogle": "gcid:dentist",
"additionalCategories": [],
"attributes": [
{
"id": "attributes/wi_fi",
"value": true
}
],
"services": [
{
"name": "Teeth Whitening",
"description": null,
"price": 150,
"currency": "USD",
"googleServiceTypeId": null
}
],
"ownerName": null,
"menuUrl": null,
"yearEstablished": null,
"tags": [
"vip"
],
"labels": [],
"regularHours": [
{
"day": "MONDAY",
"closed": false,
"periods": [
{
"openTime": "09:00",
"closeTime": "17:00"
}
]
}
],
"moreHours": [],
"specialHours": [],
"mediaByCategory": {
"EXTERIOR": [
{
"url": "https://cdn.synup.com/media/1.jpg"
}
]
},
"clientId": "cli_123",
"archived": false,
"scheduledArchiveAt": null,
"verificationStatus": "verified"
}
}Edits an existing location's business details — address, categories, Google Business Profile attributes, services, and hours. Pass only the fields you want to change; a field you do pass replaces its current value entirely (an empty string clears a text field, a full new list replaces an existing one). The country can't be changed after creation. Saved changes are automatically queued to sync out to the connected directories and listings.
Update a location
/api/v1/locations/{id}{
"data": {
"location": {
"id": "loc_456",
"name": "Acme Dental — Downtown",
"tagline": "Gentle care, on time",
"city": "Austin",
"state": "TX",
"clientId": "cli_123"
},
"rejectedAttributes": []
}
}The Google Business Profile services this location offers.
List a location's services
/api/v1/locations/{id}/services{
"data": {
"services": [
{
"name": "Teeth Whitening",
"description": null,
"price": 150,
"currency": "USD",
"googleServiceTypeId": null
}
]
}
}Adds one service to the location's list and re-publishes the full list to Google (Google has no atomic add — every save replaces the whole array).
Add a service
/api/v1/locations/{id}/services{
"data": {
"services": [
{
"name": "Teeth Whitening",
"description": null,
"price": 150,
"currency": "USD",
"googleServiceTypeId": null
}
]
}
}Removes one service by name and re-publishes the remaining list to Google.
Remove a service
/api/v1/locations/{id}/services{
"data": {
"services": []
}
}SCHEDULES a location to archive at the end of the current billing period. The location stays fully active, billable, and still counts against the plan's location limit until then — nothing is deleted, and scheduling does not free room to add another location until it actually archives. Call cancel-archive to call this off before it lands, or reactivate afterward to bring the location back.
Schedule a location to archive
/api/v1/locations/{id}/archive{
"data": {
"scheduled": true,
"scheduledArchiveAt": "2026-02-01T00:00:00.000Z",
"archiveAt": "2026-03-01T00:00:00.000Z"
}
}Calls off a pending location archival, so a location scheduled to archive at the end of the billing period carries on as normal. Only works while the archival is still pending — a location that has already archived has to be reactivated instead.
Cancel a scheduled archival
/api/v1/locations/{id}/cancel-archive{
"data": {
"cancelled": true
}
}Restores an already-archived location — and every listing under it — and clears any pending archival schedule. A location that isn't currently archived is a no-op success, not an error.
Reactivate an archived location
/api/v1/locations/{id}/reactivate{
"data": {
"archived": false,
"cancelledScheduledPosts": 0
}
}Returns aggregate location counts across your agency (or one client), broken down by status, package tier, and verification state.
Get locations summary
/api/v1/locations/summary{
"data": {
"total": 12,
"byStatus": {
"approved": 10,
"pending_verification": 1,
"archival_pending": 1
},
"byVerification": {
"verified": 9,
"pending": 1,
"unknown": 2
}
}
}{
"data": {
"id": "tag_789",
"name": "VIP",
"clientId": "cli_123"
}
}{
"data": {
"locationsUnassigned": 3
}
}{
"data": {
"added": [
"loc_456"
],
"skipped": []
}
}{}Searches the general business category taxonomy (mirrors Google's, thousands of entries) — global reference data, not scoped to your agency. The returned id is a Category id to set as a location's categoryId on create or update. For a specific publisher's own catalog, use GET /api/v1/locations/publisher-categories instead. Omit search entirely to list the full taxonomy, uncapped.
Search the general category taxonomy
/api/v1/locations/categories{
"data": {
"categories": [
{
"id": "cat_dentist",
"name": "Dentist"
},
{
"id": "cat_orthodontist",
"name": "Orthodontist"
}
]
}
}Searches one publisher's own (GABF) category catalog — global reference data, not scoped to your agency. Use the returned id for a location's publisherCategories on create or update. Omit query entirely to list the publisher's whole catalog, uncapped.
Search a publisher's own category catalog
/api/v1/locations/publisher-categories{
"data": {
"categories": [
{
"id": "gcid:dentist",
"displayName": "Dentist"
}
]
}
}Lists locations together with their photos, newest first — same filters as GET /api/v1/locations. Use this for questions like "which locations have no photos?", rather than GET /api/v1/media, which returns only ONE location's media at a time.
List locations with their media
/api/v1/locations/media{
"data": {
"locations": [
{
"id": "loc_456",
"name": "Acme Dental — Downtown",
"logoUrl": null,
"mediaByCategory": {
"EXTERIOR": [
{
"url": "https://cdn.synup.com/media/1.jpg"
}
]
},
"total": 1
}
],
"nextCursor": null,
"total": 1
}
}Adds one or more photos to a location, in a given category. Provide each image as a public https URL (fetched and re-hosted) or as base64 bytes. Only images are supported (jpeg, png, gif, webp), max 5 MB each. The LOGO category holds a single logo — uploading to LOGO replaces the current one. Every other category appends. Saved photos are automatically queued to publish to Google.
Upload photos to a location
/api/v1/locations/{id}/media{
"data": {
"mediaByCategory": {
"EXTERIOR": [
{
"url": "https://cdn.synup.com/media/1.jpg",
"source": "agent_upload"
}
]
},
"added": [
{
"category": "EXTERIOR",
"url": "https://cdn.synup.com/media/1.jpg"
}
]
}
}Removes photos from a location. Pass urls to delete specific photos, or category to clear a whole category. The logo can never be deleted through this endpoint — by url or by category — upload a new LOGO image to replace it instead. This removes the photos in the app; it does not delete them from Google.
Delete a location's photos
/api/v1/locations/{id}/media{
"data": {
"mediaByCategory": {
"EXTERIOR": []
},
"removed": [
"https://cdn.synup.com/media/1.jpg"
]
}
}