Locations

5 endpoints

Places a campaign can target — a circle or a polygon. A location is only ever created by importing a GeoJSON FeatureCollection (see the import endpoint); this resource lists, reads, updates and deletes ones already imported.

List locations#

GET/locations
Needs locations:read

Newest first. Each comes back with its shape as GeoJSON — a Point with a radius_meters for a circle, a Polygon otherwise — and the names of any location groups it belongs to.

Query parameters

cursor
string

An opaque value from a previous page's meta.next_cursor. Omit for the first page.

limit
integer

How many to return, up to 100.

Default: 20
updated_since
string (date-time)

Only locations updated at or after this ISO 8601 timestamp.

Responses

200
200 response
{
  "data": [
    {
      "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001",
      "external_id": "store-014",
      "name": "Harbour Coffee, Quay Street",
      "address": "12 Quay Street, Bristol BS1 4DB",
      "type": "circle",
      "geometry": {
        "type": "Point",
        "coordinates": [
          -2.5879,
          51.4545
        ]
      },
      "radius_meters": 150,
      "groups": [
        "Bristol stores"
      ],
      "created_at": "2026-09-20T09:12:00+00:00",
      "updated_at": "2026-09-27T14:03:00+00:00"
    }
  ],
  "meta": {
    "next_cursor": null
  }
}
Response fields
data
LocationResource[]
required
data[].id
string
required
data[].external_id
string | null
required
data[].name
string
required
data[].address
string | null
required
data[].type
string
required
data[].geometry
any[] | null
required
data[].radius_meters
integer | null
required
data[].groups
any[]
data[].created_at
string | null
required
data[].updated_at
string | null
required
meta
object
required
meta.next_cursor
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
422

Something in the request isn't valid. fields says what, with the dashboard's wording.

invalid_parameterinvalid_cursor
  • invalid_parameter

    updated_since must be a valid date.

  • invalid_cursor

    That cursor is invalid or has expired.

422 response
{
  "error": {
    "code": "invalid_parameter",
    "message": "updated_since must be a valid date.",
    "fields": {
      "updated_since": [
        "Must be a valid date."
      ]
    }
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl 'https://api.bubbl.tech/platform/v1/locations?limit=20' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/locations?limit=20', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer bubbl_YOUR_TOKEN',
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'GET',
    'https://api.bubbl.tech/platform/v1/locations?limit=20',
    headers={
        'Authorization': 'Bearer bubbl_YOUR_TOKEN',
        'Accept': 'application/json',
    },
)
response.raise_for_status()
data = response.json()
Request
<?php

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.bubbl.tech/platform/v1/locations?limit=20', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Get a location#

GET/locations/{location}
Needs locations:read

Path parameters

location
string (uuid)
required

The location ID

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001",
    "external_id": "store-014",
    "name": "Harbour Coffee, Quay Street",
    "address": "12 Quay Street, Bristol BS1 4DB",
    "type": "circle",
    "geometry": {
      "type": "Point",
      "coordinates": [
        -2.5879,
        51.4545
      ]
    },
    "radius_meters": 150,
    "groups": [
      "Bristol stores"
    ],
    "created_at": "2026-09-20T09:12:00+00:00",
    "updated_at": "2026-09-27T14:03:00+00:00"
  }
}
Response fields
data
object
required
data.id
string
required
data.external_id
string | null
required
data.name
string
required
data.address
string | null
required
data.type
string
required
data.geometry
any[] | null
required
data.radius_meters
integer | null
required
data.groups
any[]
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl 'https://api.bubbl.tech/platform/v1/locations/{location}' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/locations/{location}', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer bubbl_YOUR_TOKEN',
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'GET',
    'https://api.bubbl.tech/platform/v1/locations/{location}',
    headers={
        'Authorization': 'Bearer bubbl_YOUR_TOKEN',
        'Accept': 'application/json',
    },
)
response.raise_for_status()
data = response.json()
Request
<?php

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.bubbl.tech/platform/v1/locations/{location}', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Update a location#

PATCH/locations/{location}
Needs locations:write

Only its name, address and external_id — not its shape. To change a location's shape, re-import it with the same properties.external_id (see the import endpoint).

Path parameters

location
string (uuid)
required

The location ID

Headers

Idempotency-Key
string

A unique value (e.g. a UUID) for this change. A retry with the same key within 24 hours gets the first response back, with Idempotent-Replayed: true, instead of making the change again.

Request body

application/jsonUpdateLocationRequest
name
string
address
string | null
external_id
string | null

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001",
    "external_id": "store-014",
    "name": "Harbour Coffee, Quay Street",
    "address": "12 Quay Street, Bristol BS1 4DB",
    "type": "circle",
    "geometry": {
      "type": "Point",
      "coordinates": [
        -2.5879,
        51.4545
      ]
    },
    "radius_meters": 150,
    "groups": [
      "Bristol stores"
    ],
    "created_at": "2026-09-20T09:12:00+00:00",
    "updated_at": "2026-09-27T14:03:00+00:00"
  }
}
Response fields
data
object
required
data.id
string
required
data.external_id
string | null
required
data.name
string
required
data.address
string | null
required
data.type
string
required
data.geometry
any[] | null
required
data.radius_meters
integer | null
required
data.groups
any[]
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
409

A request with the same Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.

request_in_progress
  • request_in_progress

    This request is still being handled. Try again shortly.

409 response
{
  "error": {
    "code": "request_in_progress",
    "message": "This request is still being handled. Try again shortly."
  }
}
422

Something in the request isn't valid. fields says what, with the dashboard's wording.

validation_failed
  • validation_failed

    The given data was invalid.

422 response
{
  "error": {
    "code": "validation_failed",
    "message": "The given data was invalid.",
    "fields": {
      "name": [
        "The name field is required."
      ]
    }
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl -X PATCH 'https://api.bubbl.tech/platform/v1/locations/{location}' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  --data '{
  "name": "Harbour Coffee, Quay Street",
  "address": "12 Quay Street, Bristol BS1 4DB",
  "external_id": "store-014"
}'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/locations/{location}', {
  method: 'PATCH',
  headers: {
    Authorization: 'Bearer bubbl_YOUR_TOKEN',
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Harbour Coffee, Quay Street',
    address: '12 Quay Street, Bristol BS1 4DB',
    external_id: 'store-014',
  }),
});

const data = await response.json();
Request
import requests

response = requests.request(
    'PATCH',
    'https://api.bubbl.tech/platform/v1/locations/{location}',
    headers={
        'Authorization': 'Bearer bubbl_YOUR_TOKEN',
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    json={
        'name': 'Harbour Coffee, Quay Street',
        'address': '12 Quay Street, Bristol BS1 4DB',
        'external_id': 'store-014',
    },
)
response.raise_for_status()
data = response.json()
Request
<?php

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://api.bubbl.tech/platform/v1/locations/{location}', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'name' => 'Harbour Coffee, Quay Street',
        'address' => '12 Quay Street, Bristol BS1 4DB',
        'external_id' => 'store-014',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Delete a location#

DELETE/locations/{location}
Needs locations:write

Path parameters

location
string (uuid)
required

The location ID

Headers

Idempotency-Key
string

A unique value (e.g. a UUID) for this change. A retry with the same key within 24 hours gets the first response back, with Idempotent-Replayed: true, instead of making the change again.

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001",
    "deleted": true
  }
}
Response fields
data
object
required
data.id
string
required
data.deleted
boolean
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
409

A request with the same Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.

request_in_progress
  • request_in_progress

    This request is still being handled. Try again shortly.

409 response
{
  "error": {
    "code": "request_in_progress",
    "message": "This request is still being handled. Try again shortly."
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl -X DELETE 'https://api.bubbl.tech/platform/v1/locations/{location}' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/locations/{location}', {
  method: 'DELETE',
  headers: {
    Authorization: 'Bearer bubbl_YOUR_TOKEN',
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'DELETE',
    'https://api.bubbl.tech/platform/v1/locations/{location}',
    headers={
        'Authorization': 'Bearer bubbl_YOUR_TOKEN',
        'Accept': 'application/json',
    },
)
response.raise_for_status()
data = response.json()
Request
<?php

$client = new \GuzzleHttp\Client();

$response = $client->request('DELETE', 'https://api.bubbl.tech/platform/v1/locations/{location}', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Import locations#

POST/locations/import
Needs locations:write

A GeoJSON FeatureCollection. Each feature needs properties.name; a Point geometry also needs properties.radius (metres, 50–1000) and becomes a circle, a Polygon becomes a polygon. properties.external_id makes a re-import update the same location instead of creating a new one; properties.group adds it to a location group (created if it doesn't exist).

The response gives one result per feature — created, updated, skipped (over the plan's location limit) or failed, with reasons for the latter two. Send ?dry_run=1 to see what would happen without writing anything, and an Idempotency-Key header so a retried request after a lost response doesn't import the same features twice.

Location import must be in the company's plan (it isn't on Free): otherwise 403 feature_not_in_plan, dry runs included.

Headers

Idempotency-Key
string

A unique value (e.g. a UUID) for this change. A retry with the same key within 24 hours gets the first response back, with Idempotent-Replayed: true, instead of making the change again.

Request body

application/jsonImportLocationsRequestrequired
type
string
required
One of: FeatureCollection
features
object[]
required

1 to 1,000 GeoJSON Features. A feature that isn't valid fails on its own (see the response's results); it doesn't fail the request.

features[].type
string
required
One of: Feature
features[].geometry
object
required
features[].geometry.type
string
required

Point (with properties.radius) becomes a circle; Polygon becomes a polygon.

One of: Point, Polygon
features[].geometry.coordinates
string[]
required

[longitude, latitude] for a Point; one ring of 3–99 [longitude, latitude] points for a Polygon.

features[].properties
object
required
features[].properties.name
string
required

The location's name, up to 255 characters.

features[].properties.radius
number

A Point's radius in metres, 50–1000.

features[].properties.external_id
string

Your own id. A re-import with the same one updates that location instead of creating another.

features[].properties.group
string

A location group to add it to, created if it doesn't exist.

dry_run
boolean

Responses

200
200 response
{
  "data": {
    "dry_run": false,
    "results": [
      {
        "index": 0,
        "status": "created",
        "name": "Riverside Store",
        "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d2001",
        "reasons": null
      },
      {
        "index": 1,
        "status": "failed",
        "name": null,
        "reasons": [
          "Name is missing (properties.name)"
        ]
      }
    ],
    "summary": {
      "created": 1,
      "updated": 0,
      "skipped": 0,
      "failed": 1
    }
  }
}
Response fields
data
object
required
data.dry_run
boolean
required
data.results
object[]
required
data.results[].index
string
required
data.results[].status
"failed" | "skipped" | string
required
One of: updated, created
data.results[].name
string
required
data.results[].id
any | null | string | null | string
required
data.results[].reasons
any[] | any | null
required
data.summary
object
required
data.summary.created
integer
required
data.summary.updated
integer
required
data.summary.skipped
integer
required
data.summary.failed
integer
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
409

A request with the same Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.

request_in_progress
  • request_in_progress

    This request is still being handled. Try again shortly.

409 response
{
  "error": {
    "code": "request_in_progress",
    "message": "This request is still being handled. Try again shortly."
  }
}
422

Something in the request isn't valid. fields says what, with the dashboard's wording.

validation_failed
  • validation_failed

    The given data was invalid.

422 response
{
  "error": {
    "code": "validation_failed",
    "message": "The given data was invalid.",
    "fields": {
      "name": [
        "The name field is required."
      ]
    }
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl -X POST 'https://api.bubbl.tech/platform/v1/locations/import' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  --data '{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          -2.5879,
          51.4545
        ]
      },
      "properties": {
        "name": "Harbour Coffee, Quay Street",
        "radius": 150,
        "external_id": "store-014",
        "group": "Bristol stores"
      }
    }
  ]
}'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/locations/import', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer bubbl_YOUR_TOKEN',
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [
            -2.5879,
            51.4545,
          ],
        },
        properties: {
          name: 'Harbour Coffee, Quay Street',
          radius: 150,
          external_id: 'store-014',
          group: 'Bristol stores',
        },
      },
    ],
  }),
});

const data = await response.json();
Request
import requests

response = requests.request(
    'POST',
    'https://api.bubbl.tech/platform/v1/locations/import',
    headers={
        'Authorization': 'Bearer bubbl_YOUR_TOKEN',
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    json={
        'type': 'FeatureCollection',
        'features': [
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': [
                        -2.5879,
                        51.4545,
                    ],
                },
                'properties': {
                    'name': 'Harbour Coffee, Quay Street',
                    'radius': 150,
                    'external_id': 'store-014',
                    'group': 'Bristol stores',
                },
            },
        ],
    },
)
response.raise_for_status()
data = response.json()
Request
<?php

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.bubbl.tech/platform/v1/locations/import', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'type' => 'FeatureCollection',
        'features' => [
            [
                'type' => 'Feature',
                'geometry' => [
                    'type' => 'Point',
                    'coordinates' => [
                        -2.5879,
                        51.4545,
                    ],
                ],
                'properties' => [
                    'name' => 'Harbour Coffee, Quay Street',
                    'radius' => 150,
                    'external_id' => 'store-014',
                    'group' => 'Bristol stores',
                ],
            ],
        ],
    ],
]);

$data = json_decode((string) $response->getBody(), true);