Source: https://developers.woosmap.com/products/stores-api/features/data-management/

> For clean Markdown of any page, append `.md` to the page URL.

> For a complete documentation index, see https://developers.woosmap.com/llms.txt

# Data Management



**Complete API Specification** : [Data Management API Reference](/api-reference/data-api/post-stores/)

## What is the Data Management Endpoint?

Manage the store and asset data within your Woosmap project. It provides full CRUD (Create, Read, Update, Delete) operations through standard HTTP methods, supporting batch imports up to 15MB, along with an atomic replace operation for swapping entire datasets without downtime.

### What It Does

This endpoint handles the complete lifecycle of your location data. You can add new assets in batch using `POST`, update existing assets with `PUT`, retrieve assets as [GeoJSON](https://geojson.org/) with `GET`, remove individual or all assets with `DELETE`, and atomically replace your entire dataset using the replace operation. All write operations accept JSON payloads containing an array of store objects, each with a unique `storeId`, name, and geographic coordinates.

### Key Characteristics

Data Management is designed for server-side workflows where you need to populate, maintain, and synchronize your location data. All write operations (`POST`, `PUT`, `DELETE`) require a **private key** and are strictly server-side. Read operations (`GET`) can use either a private or public key. Batch operations are atomic per request, if any asset in a batch fails validation, the entire batch is rejected. The `storeId` you assign to each asset is your stable identifier used across all operations.

## When to Use This Endpoint

Use Data Management to import your stores and locations into Woosmap, keep your asset data in sync with your internal systems, remove outdated locations, or perform full dataset replacements during scheduled data refreshes. It is the entry point for populating the data that powers [Search](/products/stores-api/features/search/) and [Autocomplete](/products/stores-api/features/autocomplete/).

Don’t use Data Management for searching or querying your assets; that’s what [Search](/products/stores-api/features/search/) is for. If you need users to find stores interactively, use [Autocomplete](/products/stores-api/features/autocomplete/) instead.

New to the API? Start with the [Making Your First Request](/products/stores-api/tutorials/making-first-request/) tutorial or follow the [Integration Path](/products/stores-api/guides/integration-path/) guide for the recommended setup workflow.

## API Endpoint

```http
POST|GET|PUT|DELETE https://api.woosmap.com/stores
```

### Authentication

Authenticate using either a `key` (public API key for client-side read requests) or `private_key` (required for all write operations). Private keys should be kept secret and never exposed in client code. You can also use the `X-Api-Key` header for server-side or mobile authentication.

| Key | Description |
| --- | --- |
| private\_key | A private key is used for server api calls (`POST`, `PUT` and `DELETE`) where modification of the data occurs. |
| key | A public key is used client side to fetch your project data. |

The private key can modify and delete data, so it **must** remain **private**. The public key can be made public since it’s used client side.

```shell
# Server-side (write operations - query parameter)
https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_KEY

# Client-side (read only - query parameter)
https://api.woosmap.com/stores?key=YOUR_PUBLIC_KEY

# Server-side / Mobile (header)
curl -H "X-Api-Key: YOUR_PRIVATE_KEY" https://api.woosmap.com/stores
```

For complete authentication details and security best practices, see [API Keys Documentation](/api-reference/authentication/).

## Operations Overview

The Data Management endpoint supports five operations via standard HTTP methods:

| Operation | Method | Description |
| --- | --- | --- |
| [Add Assets](#add-assets) | `POST` | Create new assets in batch |
| [Update Assets](#update-assets) | `PUT` | Modify existing assets in batch |
| [Delete Assets](#delete-assets) | `DELETE` | Remove one, several, or all assets |
| [Get Assets](#get-assets) | `GET` | Retrieve all assets or a specific asset by storeId |
| [Replace Assets](#replace-assets) | `POST` | Atomically replace all assets with a new dataset |

All write operations require the `private_key` parameter. The request body for `POST`, `PUT`, and replace must be a JSON object with a `stores` array. Each element must follow the [data structure](#data-structure) defined below.

### Request Body Format

```json
{
  "stores": [
    {
      "storeId": "unique identifier",
      "name": "Store Name",
      "location": {
        "lat": 43.6,
        "lng": 3.883
      }
    }
  ]
}
```

For a more complete store example including types, tags, user properties, and opening hours, see the [API Reference](/api-reference/data-api/post-stores/).

## Data Structure

### Required Data

An asset element has the following minimal structure ([more complete store example](/api-reference/data-api/post-stores/)):

```json
{
  "storeId": "unique identifier",
  "name": "Store Name",
  "location": {
    "lat": 43.6,
    "lng": 3.883
  }
}
```

`storeId`, `name` and `location` are **mandatory** , everything else is optional.

The `storeId` you assign in request bodies (camelCase) is returned as `store_id` (snake\_case) in API responses. When filtering via the [Query Syntax](/products/stores-api/concepts/query-syntax/), use the field name `idstore`.

The `storeId` field must be a unique identifier for your store and match the following regexp `[a-zA-Z0-9_]+`. Or in English, uppercase and lowercase alphanumeric characters and underscores.

### Optional Data

`types` : An array of strings, with the types of the store.

```json
{
  "types": ["book_store", "library"]
}
```

`tags`: An array of strings, of useful tags for filtering and grouping.

```json
{
  "tags": ["science", "comics"]
}
```

`address`: An address object for the store.

```json
{
  "address": {
    "lines": ["Road name", "Another line of address"],
    "countryCode": "FR",
    "city": "Paris",
    "zipcode": "75000"
  }
}
```

`contact`: An object for the contact details of your asset, with optional `website`, `phone` and `email` values.

```json
{
  "contact": {
    "website": "website url",
    "phone": "phone number",
    "email": "contact email"
  }
}
```

`openingHours`: An object to define the opening times of your store. In this example the store will be open between `8:30am` and `7:30pm` every day except sunday (`7`) where it’ll be closed. The `december 24th 2015` the store will be open from `9:00am` to `6:00pm` and the `december 25th 2015` the store will be closed.

For more details concerning Open Hours see the dedicated page [Opening Hours](/products/stores-api/concepts/opening-hours/).

```json
{
  "openingHours": {
    "timezone": "Europe/Paris",
    "usual": {
      "7": [],
      "default": [{ "start": "08:30", "end": "19:30" }]
    },
    "special": {
      "2015-12-24": [{ "start": "09:00", "end": "18:00" }],
      "2015-12-25": []
    }
  }
}
```

`userProperties`: An object containing additional user-defined data.

```json
{
  "rating": 5,
  "internal_id": "123abc"
}
```

`localizedNames`: You can define alternate names for your assets. These localized names are useful for multi-language integrations. Combined with the [Autocomplete](/products/stores-api/features/autocomplete/) endpoint, you can let your users find your store in their native language.

```json
{
  "localizedNames": {
    "ar": "\u0645\u0631\u0643\u0632 \u0641\u064a\u0644\u064a\u062a\u0632\u064a \u0627\u0644\u062a\u062c\u0627\u0631\u064a",
    "fr": "Centre Commercial Velizy",
    "it": "Centro Commerciale Velizy",
    "gb": "Velizy Shopping Center",
    "id": "Pusat Perbelanjaan Velizy",
    "ko": "\ubc38\ub9ac\uc9c0 \uc1fc\ud551\uc13c\ud130",
    "sv": "V\u00e9lizy, galleria",
    "th": "\u0e28\u0e39\u0e19\u0e22\u0e4c\u0e2a\u0e23\u0e23\u0e1e\u0e2a\u0e34\u0e19\u0e04\u0e49\u0e32 Velizy Shopping Center",
    "tr": "Velizy Al\u0131\u015fveri\u015f Merkezi"
  }
}
```

## Add Assets

Create assets in a batch.

**Warning** : `storeId` must not exist when using `POST` method, if one store already exists, the batch will be refused.

```shell
curl -L 'https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY' \
-H 'content-type: application/json' \
-d '{
  "stores": [
    {
      "storeId": "store_123",
      "name": "My first cool store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
}'
```

```python
import requests
import json

url = "https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY"

payload = json.dumps({
    "stores": [
        {
            "storeId": "store_123",
            "name": "My first cool store",
            "location": {
                "lat": 43.61,
                "lng": 3.88
            }
        }
    ]
})
headers = {
    'content-type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
```

```text
https://api.woosmap.com/stores
  ?private_key=YOUR_PRIVATE_API_KEY
```

```javascript
const axios = require('axios');
let data = JSON.stringify({
  "stores": [
    {
      "storeId": "store_123",
      "name": "My first cool store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
});

let config = {
  method: 'post',
  maxBodyLength: Infinity,
  url: 'https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY',
  headers: { 
    'content-type': 'application/json'
  },
  data : data
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
```

```java
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"stores\": [\n    {\n      \"storeId\": \"store_123\",\n      \"name\": \"My first cool store\",\n      \"location\": {\n        \"lat\": 43.61,\n        \"lng\": 3.88\n      }\n    }\n  ]\n}");
Request request = new Request.Builder()
  .url("https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY")
  .method("POST", body)
  .addHeader("content-type", "application/json")
  .build();
Response response = client.newCall(request).execute();
```

```ruby
require "uri"
require "json"
require "net/http"

url = URI("https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Post.new(url)
request["content-type"] = "application/json"
request.body = JSON.dump({
  "stores": [
    {
      "storeId": "store_123",
      "name": "My first cool store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
})

response = https.request(request)
puts response.read_body
```

### Complete Sample

For a more complete store example, see the [API Reference](/api-reference/data-api/post-stores/).

## Update Assets

Modify existing assets. You can modify any field. Location, name, types, tags, user properties, opening hours, and more.

**Warning** : `storeId` must exist when using `PUT` method, if one asset does not exist, the batch will be refused.

```shell
curl -L -X PUT 'https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY' \
-H 'content-type: application/json' \
-d '{
  "stores": [
    {
      "storeId": "store_123",
      "name": "My amazing store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
}'
```

```python
import requests
import json

url = "https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY"

payload = json.dumps({
    "stores": [
        {
            "storeId": "store_123",
            "name": "My amazing store",
            "location": {
                "lat": 43.61,
                "lng": 3.88
            }
        }
    ]
})
headers = {
    'content-type': 'application/json'
}

response = requests.request("PUT", url, headers=headers, data=payload)

print(response.text)
```

```text
https://api.woosmap.com/stores
  ?private_key=YOUR_PRIVATE_API_KEY
```

```javascript
const axios = require('axios');
let data = JSON.stringify({
  "stores": [
    {
      "storeId": "store_123",
      "name": "My amazing store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
});

let config = {
  method: 'put',
  maxBodyLength: Infinity,
  url: 'https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY',
  headers: { 
    'content-type': 'application/json'
  },
  data : data
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
```

```java
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"stores\": [\n    {\n      \"storeId\": \"store_123\",\n      \"name\": \"My amazing store\",\n      \"location\": {\n        \"lat\": 43.61,\n        \"lng\": 3.88\n      }\n    }\n  ]\n}");
Request request = new Request.Builder()
  .url("https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY")
  .method("PUT", body)
  .addHeader("content-type", "application/json")
  .build();
Response response = client.newCall(request).execute();
```

```ruby
require "uri"
require "json"
require "net/http"

url = URI("https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_API_KEY")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Put.new(url)
request["content-type"] = "application/json"
request.body = JSON.dump({
  "stores": [
    {
      "storeId": "store_123",
      "name": "My amazing store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
})

response = https.request(request)
puts response.read_body
```

The restrictions on `POST` and `PUT` on `storeIds` are made to ensure identifier stability.

## Delete Assets

Remove one or more assets using the `DELETE` method. The `storeId` is the id you defined. To delete several assets, use a comma to separate the IDs.

```shell
curl -L -X DELETE 'https://api.woosmap.com/stores/?query=idstore%3A%3Dstore_123&private_key=YOUR_PRIVATE_API_KEY'
```

```python
import requests

url = "https://api.woosmap.com/stores/?query=idstore%3A%3Dstore_123&private_key=YOUR_PRIVATE_API_KEY"

payload = {}
headers = {}

response = requests.request("DELETE", url, headers=headers, data=payload)

print(response.text)
```

```text
https://api.woosmap.com/stores/
  ?private_key=YOUR_PRIVATE_API_KEY
  &query=idstore%3A%3Dstore_123
```

```javascript
const axios = require('axios');

let config = {
  method: 'delete',
  maxBodyLength: Infinity,
  url: 'https://api.woosmap.com/stores/?query=idstore%3A%3Dstore_123&private_key=YOUR_PRIVATE_API_KEY',
  headers: { }
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
```

```java
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
  .url("https://api.woosmap.com/stores/?query=idstore%3A%3Dstore_123&private_key=YOUR_PRIVATE_API_KEY")
  .method("DELETE", body)
  .build();
Response response = client.newCall(request).execute();
```

```ruby
require "uri"
require "net/http"

url = URI("https://api.woosmap.com/stores/?query=idstore%3A%3Dstore_123&private_key=YOUR_PRIVATE_API_KEY")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Delete.new(url)

response = https.request(request)
puts response.read_body
```

To delete **all** assets in a project, omit the `storeId`

```shell
curl -X DELETE 'https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_KEY'
```

## Get Assets

Use the `GET` method to retrieve your data. The API returns a [GeoJSON](https://geojson.org/) response.

```shell
curl 'https://api.woosmap.com/stores?private_key=YOUR_PRIVATE_KEY'
```

You can also retrieve a specific asset by its `storeId`:

```shell
curl 'https://api.woosmap.com/stores/YOUR_STORE_ID?private_key=YOUR_PRIVATE_KEY'
```

Your assets can also be visualized with the Store Locator Widget associated with your project in the [Woosmap Console](https://console.woosmap.com/home) (Widgets tab).

For querying and filtering your assets, use the [Search Query](/products/stores-api/features/search/) endpoint.

## Replace Assets

You can replace all assets of a Project making a `POST` request on the `/stores/replace` endpoint. This will delete all previous assets and import assets from the JSON payload.

```shell
curl -L 'https://api.woosmap.com/stores/replace?private_key=YOUR_PRIVATE_API_KEY' \
-H 'content-type: application/json' \
-d '{
  "stores": [
    {
      "storeId": "store_123",
      "name": "My first cool store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
}'
```

```python
import requests
import json

url = "https://api.woosmap.com/stores/replace?private_key=YOUR_PRIVATE_API_KEY"

payload = json.dumps({
    "stores": [
        {
            "storeId": "store_123",
            "name": "My first cool store",
            "location": {
                "lat": 43.61,
                "lng": 3.88
            }
        }
    ]
})
headers = {
    'content-type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
```

```text
https://api.woosmap.com/stores/replace
  ?private_key=YOUR_PRIVATE_API_KEY
```

```javascript
const axios = require('axios');
let data = JSON.stringify({
  "stores": [
    {
      "storeId": "store_123",
      "name": "My first cool store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
});

let config = {
  method: 'post',
  maxBodyLength: Infinity,
  url: 'https://api.woosmap.com/stores/replace?private_key=YOUR_PRIVATE_API_KEY',
  headers: { 
    'content-type': 'application/json'
  },
  data : data
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
```

```java
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"stores\": [\n    {\n      \"storeId\": \"store_123\",\n      \"name\": \"My first cool store\",\n      \"location\": {\n        \"lat\": 43.61,\n        \"lng\": 3.88\n      }\n    }\n  ]\n}");
Request request = new Request.Builder()
  .url("https://api.woosmap.com/stores/replace?private_key=YOUR_PRIVATE_API_KEY")
  .method("POST", body)
  .addHeader("content-type", "application/json")
  .build();
Response response = client.newCall(request).execute();
```

```ruby
require "uri"
require "json"
require "net/http"

url = URI("https://api.woosmap.com/stores/replace?private_key=YOUR_PRIVATE_API_KEY")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Post.new(url)
request["content-type"] = "application/json"
request.body = JSON.dump({
  "stores": [
    {
      "storeId": "store_123",
      "name": "My first cool store",
      "location": {
        "lat": 43.61,
        "lng": 3.88
      }
    }
  ]
})

response = https.request(request)
puts response.read_body
```

During the operation, previous assets will still exist and so will be available for queries.

If the import fails, previous assets will not be deleted.

## Understanding the Response

The `GET` method returns your assets in [GeoJSON](https://geojson.org/) format. When retrieving all assets, the response is a `FeatureCollection` containing an array of `Feature` objects. When retrieving a single asset by `storeId`, the response is a single `Feature` object.

See the [API Reference](/api-reference/data-api/post-stores/) for a complete GeoJSON response example.

### Key Response Fields

**`type`** - Either `FeatureCollection` (all assets) or `Feature` (single asset)

**`features`** - Array of GeoJSON Feature objects (when retrieving all assets)

**`geometry`** - The geographic coordinates of the asset

```json
"geometry": {
  "type": "Point",
  "coordinates": [3.883, 43.600]
}
```

**`properties`** - The asset data including `storeId`, `name`, `types`, `tags`, `user_properties`, `contact`, `address`, and `open` (opening hours). See the [Data Structure](#data-structure) section for the complete field reference.

### Write Operation Responses

- **`POST` (Add)** - Returns `200 OK` on success with the number of imported assets.
- **`PUT` (Update)** - Returns `200 OK` on success with the number of updated assets.
- **`DELETE`** - Returns `200 OK` on success.
- **Replace** - Returns `200 OK` on success with the number of imported assets.

If any asset in a batch fails validation, the entire request is rejected with an error message indicating the issue.

## Troubleshooting

| Issue | Common Cause | Solution |
| --- | --- | --- |
| Batch rejected on `POST` | A `storeId` in the request already exists. | Use `PUT` to update existing assets, or verify your `storeId` values are unique. |
| Batch rejected on `PUT` | A `storeId` in the request does not exist. | Use `POST` to create new assets |
| Timeout errors | The process exceeds 30 seconds due to file size or data complexity | Reduce the number of assets per request to reduce the processing time. |
| Invalid JSON format | Request body does not follow the required structure of a `stores` array. | Verify each asset includes `storeId`, `name`, and `location` with `lat`/`lng`. |
| Authentication errors | Write operations (`POST`, `PUT`, `DELETE`) require a `private_key` | Public keys (`key`) only work for `GET` requests. |

## Usage Limits & Quotas

- **Rate Limit** : Maximum of 10 queries per second (QPS) per Project
- **Payload Size** : JSON file should not exceed 15MB
- Exceeding rate limits returns an error response

**if a request exceeds 30 seconds** (due to file size or data complexity), a Timeout message is returned. Reduce the number of assets in the request to lighten the process time.

## Related Features

- [Opening Hours](/products/stores-api/concepts/opening-hours/) - Format for defining store opening hours
- [Search](/products/stores-api/features/search/) - Query and filter assets by attributes, location, and zones
- [Autocomplete](/products/stores-api/features/autocomplete/) - Interactive store search with typeahead suggestions
- [Zones](/products/stores-api/features/zones/) - Define and manage geographic zones for your assets
- [Stores API Overview](/products/stores-api/overview/) - Introduction to the Stores API
- [Integration Path](/products/stores-api/guides/integration-path/) - Recommended setup workflow
- [Making Your First Request](/products/stores-api/tutorials/making-first-request/) - Step-by-step tutorial
