# Update profile

Profiles centralize data and events from multiple sources (Apps, Websites, APIs) in a single place based on the Custom ID.

Use `/profiles/update` for continuous streams and rapid processing of small batches where quick data ingestion is key. Learn more about selecting the right endpoint here: [Profile updates: choosing the right API endpoint](https://doc.batch.com/developer/api/cep/profiles/..#profile-updates-choosing-the-right-api-endpoint).

## API Description

## Update

> Update one or multiple profile's data and track events

```json
{"openapi":"3.1.0","info":{"title":"Batch - REST API","version":"2.9"},"tags":[{"name":"Profiles"}],"servers":[{"url":"https://api.batch.com/{version}","description":"production","variables":{"version":{"default":"2.11","description":"Version of the API"}}}],"security":[{"rest_key":[]}],"components":{"securitySchemes":{"rest_key":{"type":"http","scheme":"bearer","description":"## API Key Authentication\n\nAuthentication is required in order to interact with Batch's APIs.\n\nBatch implements authentication using API Keys, that we call the \"REST API Key\".\nYou can find it on your dashboard.\n\nPlease make sure that you keep this key secret. You should never use it in client apps to call APIs from there as it would\neasily be extractable.\n\n### How to authenticate\n\nIn order to authenticate your requests, add your REST API Key in the `Authorization` header and prefix it by `Bearer`. Example: `Authorization: Bearer bcd38d9rfb38ra28`.\n"}},"parameters":{"HeaderProjectKey":{"in":"header","name":"X-Batch-Project","description":"The unique project key, identifying a project on the Batch platform","schema":{"type":"string"},"required":true}},"schemas":{"BulkEditProfile":{"description":"Edit multiple profiles at once. You can specify at most 200 operations or the API call will be rejected.","type":"array","items":{"$ref":"#/components/schemas/EditProfile"}},"EditProfile":{"type":"object","required":["identifiers"],"properties":{"identifiers":{"$ref":"#/components/schemas/ProfileIdentifiers"},"attributes":{"$ref":"#/components/schemas/EditProfileAttributes"},"events":{"description":"Events to track on the profile. This array cannot weigh more than 150kB or have more than 15 elements and individual events cannot weigh more than 25kB. If one of these limits are exceeded, the complete Profile API call will be rejected.","type":"array","maxItems":15,"items":{"$ref":"#/components/schemas/TrackProfileEvent"}}}},"ProfileIdentifiers":{"description":"Specifies which profile to target. Exactly one of custom_id or installation must be provided.\n","oneOf":[{"$ref":"#/components/schemas/CustomID"},{"$ref":"#/components/schemas/Installation"}]},"CustomID":{"type":"object","required":["custom_id"],"properties":{"custom_id":{"description":"Custom user ID of the profile to perform the operation on.","type":"string","maxLength":512}}},"Installation":{"type":"object","required":["installation"],"properties":{"installation":{"$ref":"#/components/schemas/ProfileInstallationIdentifier"}}},"ProfileInstallationIdentifier":{"description":"Batch installation identifier.","type":"object","required":["apikey","installation_id"],"properties":{"apikey":{"description":"API key of the installation.","type":"string"},"installation_id":{"description":"Installation identifier generated by the SDK.","type":"string"}}},"EditProfileAttributes":{"description":"Attributes to write on the profile. Keys are the attributes names. You can set up to 50 attributes on a profile per operation: requests exceeding this limit will be ignored. Some keys are documented as they're reserved, but extra data is supported: See [the attributes documentation](https://doc.batch.com/developer/api/cep/profiles/update#the-attributes-object). This object cannot weigh more than 25kB or the entire Profile API request will be rejected.\n","type":"object","properties":{"$email_address":{"description":"Email address to write on the profile. Null to erase, omit to leave the email unmodified. Addresses must be valid, not longer than 256 characters.","type":["string","null"],"maxLength":256,"pattern":"^[^\\r\\n\\t@]+@[A-z0-9\\-\\.]+\\.[A-z0-9]+$"},"$email_marketing":{"description":"Set whether the profile should receive marketing emails or not. Set to null to reset, omit to leave unmodified.","type":["string","null"],"enum":["subscribed","unsubscribed"]},"$phone_number":{"description":"Phone number to write on the profile. Null to erase, omit to leave the phone number unmodified. The number must conform to the [E.164 international standard](https://en.wikipedia.org/wiki/E.164).","type":["string","null"]},"$sms_marketing":{"description":"Set whether the profile should receive marketing SMS or not. Set to null to reset, omit to leave unmodified.","type":["string","null"],"enum":["subscribed","unsubscribed"]},"$email_open_tracking_consent":{"description":"Set whether the profile has given consent to email open tracking. Set to null to reset, omit to leave unmodified.\n","type":["string","null"],"enum":["granted","denied"]},"$timezone":{"description":"Timezone to set on the profile. Set to null to erase, omit to leave unmodified. Value must be an IANA TZ Identifier. Example: `Europe/Paris`. You can find an non exhaustive list of available TZ identifiers [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).\n","type":["string","null"]},"$language":{"description":"Custom language to set on the profile. Set to null to erase, omit to leave unmodified. Language codes must conform to a subset of BCP 47 language tags. See [our documentation](https://doc.batch.com/api/campaigns/advanced#language-and-country-codes) for a list of supported languages.\n","type":["string","null"]},"$region":{"description":"Custom country code to set on the profile. Set to null to erase, omit to leave unmodified. Country codes must conform to the ISO 3166-1 alpha-2 standard. See [our documentation](https://doc.batch.com/api/campaigns/advanced#language-and-country-codes) for a list of supported languages.\n","type":["string","null"]},"$topic_preferences":{"description":"Subscription topics associated with the profile. Set to an array of strings to replace all topics, `null` to erase all topics, or use `$add`/`$remove` for partial updates. Each topic must be lowercase and match the pattern `[a-z0-9_-]`, with a maximum length of 300 characters. A maximum of 25 topics can be sent per request. A profile can have at most 1500 topics.\n","oneOf":[{"type":"array","description":"Replace all topics with this list.","items":{"type":"string","pattern":"^[a-z0-9_-]{1,300}$"},"maxItems":25},{"type":"null","description":"Erase all topics."},{"type":"object","description":"Partial update — add or remove topics without replacing the full list.","properties":{"$add":{"type":"array","description":"Topics to add to the profile. Elements already present will be moved to the end.","items":{"type":"string","pattern":"^[a-z0-9_-]{1,300}$"},"maxItems":25},"$remove":{"type":"array","description":"Topics to remove from the profile.","items":{"type":"string","pattern":"^[a-z0-9_-]{1,300}$"},"maxItems":25}},"additionalProperties":false}]}},"additionalProperties":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"object"},{"type":"boolean"},{"type":"array","items":{"type":"string"}}]}},"TrackProfileEvent":{"type":"object","required":["name"],"properties":{"name":{"description":"Event name. Cannot be empty, must not be longer than 30 chars and not contain any special character ([a-z0-9_]).","type":"string","pattern":"[a-z0-9_]","maxLength":30},"time":{"description":"UTC date in a RFC 3339 format. The time date and time an event occurred. You can track events that happened in the last 24 hours, and no events in the future. If this parameter is not included in your request, Batch will use the time the event arrives at the server.\n","type":"string"},"attributes":{"$ref":"#/components/schemas/TrackEventAttributes"}}},"TrackEventAttributes":{"description":"Event attributes. Keys are the attribute names. Some keys are documented as they're reserved, but extra data is supported. See [the event attributes documentation](https://doc.batch.com/developer/api/cep/profiles/update#the-events-object) for more info.","type":"object","properties":{"$label":{"type":"string","description":"Event label. Must be a string, will automatically be bridged as label for application event compatiblity.","maxLength":200},"$tags":{"type":"array","items":{"type":"string"},"description":"Event tags. Must be an array of string, will automatically be bridged as tags for application event compatiblity. Tags must not be longer than 64 characters.","maxItems":10}},"additionalProperties":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"object"},{"type":"boolean"},{"type":"array","items":{}}]}}}},"paths":{"/profiles/update":{"post":{"operationId":"profile_update","tags":["Profiles"],"summary":"Update","description":"Update one or multiple profile's data and track events","parameters":[{"$ref":"#/components/parameters/HeaderProjectKey"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkEditProfile"}}}}}}}}
```

### Rate Limiting

To ensure fair usage and platform stability, the `/profiles/update` endpoint is subject to rate limiting, following the [Token Bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm:

* **API rate:** Up to **300 profile updates per second** are allowed.
* **Burst capacity:** The API allows short bursts of up to **1000 profile updates** (tokens) at once, if your bucket is full.
* If you exceed these limits, the API will respond with an HTTP **429 Too Many Requests** error. When you receive this error, please wait a few seconds before retrying, as further attempts may continue to be rejected until your rate falls back within the allowed limits.

Note that our API rate limit is measured in **profile updates per second**.

Unlike other API rate limits which are often measured in requests per second or minute, our rate is calculated based on the number of **Custom IDs** processed within your requests.

**What constitutes one "update"?** One update corresponds to the processing associated with a single **Custom ID** included in your request. For example, if a request contains data for 10 different Custom IDs, it will consume 10 updates from your rate limit, regardless of the number of attributes updated or events tracked for each individual Custom ID within that same request.

This unit of measurement has been chosen to effectively manage the processing load, particularly when utilizing bulk operations. Processing multiple Custom IDs in a single bulk request is more efficient, and tracking the rate by the number of IDs processed (updates) ensures fair usage and system stability under heavy load from bulk submission.

## Request structure

### Route

The Profile API exposes a POST endpoint that allows to update a batch of profiles:

`/profiles/update`

You can update one or several profiles via this endpoint, within the limit of 200 profiles.

### Headers and authentication

See [Overview → Using Project APIs](https://doc.batch.com/developer/api/cep/..#request-headers-and-authentication).

### Update profile

The body of the request must contain a valid JSON payload describing the update operations to execute for a set of given Custom IDs.

```json
[
  {
    "identifiers": {
      "custom_id": "129c7819-9c88-496e-9a5f-62db34a3ce61"
    },
    "attributes": {
      "$email_address": "jane_doe@batch-store.com"
    },
    "events": [
      {
        "name": "validated_purchase",
        "attributes": {
          "delivery_address": {
            "number": 43,
            "street": "Rue Beaubourg",
            "zip_code": 75003,
            "city": "Paris",
            "country": "France"
          },
          "items_list": [
            {
              "name": "Basic Tee",
              "size": "M",
              "price": 23.99,
              "url(item_url)": "https://batch-store.com/basic-tee",
              "url(item_image)": "https://batch-store.com/basic-tee/black/image.png",
              "in_sales": true
            },
            {
              "name": "Short socks pack x3",
              "size": "38-40",
              "price": 15.99,
              "url(item_url)": "https://batch-store.com/short-socks-pack-x3",
              "url(item_image)": "https://batch-store.com/short-socks-pack-x3/image.png",
              "in_sales": false
            }
          ],
          "metadata": [
            "first_purchase",
            "apple_pay"
          ]
        }
      }
    ]
  }
]
```

To update multiple profiles at the same time:

```json
[
  {
    "identifiers": {
      "custom_id": "129c7819-9c88-496e-9a5f-62db34a3ce61"
    },
    "attributes": {
      "$email_address": "jane_doe@batch-store.com"
    }
  },
  {
    "identifiers": {
      "custom_id": "92bec35f-07fa-42d9-b676-74bb165dd018"
    },
    "attributes": {
      "$email_address": "bo_b@gmail.com"
    }
  }
]
```

## The identifiers object

The identifiers object allows you to indicate which profile you need to update. You must provide either a `custom_id` or an `installation`, but not both.

### Using a Custom ID

```json
"identifiers": {
    "custom_id": "129c7819-9c88-496e-9a5f-62db34a3ce61"
}
```

Batch supports sending attributes and events using the Custom ID, this custom identifier can be:

* The unique ID you are using in your login system.
* A stable ID used in your data store (e.g. Firebase, etc).
* Any stable information that can help you to identify a user (e.g. hashed email address, etc).

The custom\_id must have 512 characters at most.

### Using an Installation ID

{% hint style="info" %}
Note: The installation identifier is available starting from API version 2.9.
{% endhint %}

```json
"identifiers": {
    "installation": {             
        "apikey": "YOUR_APP_APIKEY",                                   
        "installation_id": "01234567-89ab-cdef-0123-456789abcdef"      
    }                             
}
```

The `apikey` corresponds to the `SDK API Key` associated with your project. You can find this key in your Batch Dashboard by navigating to: Settings → General.

#### Restricted Fields

When the Installation ID is used as the identifier, some profile-level attributes cannot be updated and will be ignored if included in the request.

Affected attributes:

* `$email_address`
* `$email_marketing`
* `$phone_number`
* `$sms_marketing`

please note that using a non-existing `installation_id` **will not create a profile**.

## The attributes object

The Profile API allows you to set and update native and custom profile attributes in the attributes object. It has the following form:

```json
"attributes": {
      "$email_address": "jane.doe@demobatch.com",
      "$email_marketing": "subscribed",
      "$phone_number": "+33182837140",
      "$sms_marketing": "unsubscribed",
      "$language": "en",
      "$region": "FR",
      "$timezone": "Europe/Paris",
      "firstname": "Jane",
      "date(birthdate)": "1989-07-20T00:00:00Z",
      "interests": [
        "bikes",
        "cinema"
      ],
      "reward_programs": {
        "$add": [
          "premium_customer"
        ]
      }
}
```

Attribute keys refer to the names of the attributes. Set the attribute value by providing its name (key) and the value associated. You can set up to 50 attributes per profile per operation: requests exceeding this limit will be ignored. Some keys are documented as they're reserved. This object cannot weigh more than 25kB or the entire Profile API request will be rejected.

#### Native attributes

Native attributes have reserved keys, and are all prefixed by a *$* sign. You cannot set a custom attribute starting by a *$* sign.

#### Custom attributes

You can also set custom attributes, with a name and a value.

Key names must be made of letters, numbers or underscores (\[a-z0-9\_]). They cannot be longer than 30 characters and cannot contain uppercase characters.

The following value types are supported:

* String
* Integer
* Float
* Date
* URL
* Boolean
* String arrays

#### String

String types attributes cannot be empty or over 300 characters, they can be *null*. Setting a string to *null* completely removes this attribute from the profile.

```json
{"firstname": "Jane"}
```

#### Integer

Integer attributes are supported and can be *null*. Setting an integer to *null* completely removes this attribute from the profile.

```json
{"age": 25}
```

#### Float

Float attributes are supported and can be *null*. Setting a float to *null* completely removes this attribute from the profile.

```json
{"level_progress": 25.5}
```

#### Date

JSON does not support date natively. Therefore, we require additional information to identify an attribute as a date. This is achieved by wrapping the attribute key in the `date()` function.

Dates are accepted in TS format in seconds and RFC 3339 format (2012-08-12T22:30:05Z).

```json
{
  "date(promo_starts)": 1451642400,
  "date(promo_ends)": "2012-08-12T22:30:05Z"
}
```

#### URL

URL type is also supported. You will need to wrap the key name in the `url()` function.

Values must be valid URLs and no longer than 2048 characters:

* A scheme is compulsory (myapp\://, https\://...)
* :// is compulsory after the scheme

```json
{
  "url(product_image)": "https://batchstore/product/4729/image.png",
  "url(product_deeplink)": "myapp://path/to/content"
}
```

#### Boolean

Boolean attributes can be *true*, *false* or *null*.

```json
{
  "is_premium": false
}
```

#### String arrays

Arrays of strings are supported at a profile level. A string in an array cannot be longer than 300 chars. When updating a profile via the API, a string array can contain up to **25 attributes per request**. You can:

* set directly all attributes of an array
* add an attribute to an array, with `$add`
* remove an attribute from an array, with `$remove`
* delete an array from a profile by setting its value to `null`

If some attribute is invalid in the array, the whole array will be rejected and not set on the profile.

You can add an attribute to a non-existing array, Batch will automatically create the array if it doesn't exist yet on the targeted profile.

Using the `$add` function will replace any existing attributes with the same name by an array with the set array item. If you use the `$remove` operation, it will delete the attribute from the profile.

```json
"interests": ["bikes","cinema"],
"reward_programs": {
    "$add": [
        "premium_customer"
    ]
}
```

**Limits and Behavior:** A string array attribute stored in a Profile can contain up to **1500 attributes** in total. When this limit is exceeded, the oldest attributes are automatically removed to keep the array at 1500 elements. Arrays of strings have the following properties:

* **Uniqueness** of elements.
* **Deterministic order**: when an element is added, it is placed at the end of the array; if it already existed, it is moved to the end.

#### Deleting an attribute

To delete an attribute, set its value to null:

```json
"firstname": null,
"promo_starts": null,
```

Date/URL attributes are deleted in the same way and must not have their name wrapped in `date()/url()`.

## The events object

The events object contains all events to track on the profile. It cannot weigh more than 125kB or contain more than 15 events. You can set up to 15 events per profile per API call. If one of these limits is exceeded, the whole Profile API call will be rejected.

#### The events attributes object

The event attributes object allows you to attach additional attributes to an event, it cannot me more than 25kB.

It has the following structure:

```json
"attributes": {
  "items_list": [
    {
      "name": "Basic Tee",
      "size": "M",
      "price": 23.99,
      "url(item_url)": "https://batch-store.com/basic-tee",
      "url(item_image)": "https://batch-store.com/basic-tee/image.png",
      "in_sales": true
      },
      {
        "name": "Short socks pack x3",
        "size": "38-40",
        "price": 15.99,
        "url(item_url)": "https://batch-store.com/short-socks-pack-x3",
        "url(item_image)": "https://batch-store.com/short-socks-pack-x3/image.png",
        "in_sale": false
      }
    ],
    "delivery_address": {
      "number": 43,
      "street": "Rue Beaubourg",
      "zip_code": 75003,
      "city": "Paris",
      "country": "France"
    },
    "metadata": [
      "first_purchase",
      "quick_buy"
    ]
  }
```

The following value types are supported:

* String
* Number
* Float
* Date
* URL
* Array
* Object

All types except for Array & Object behave as they do in [profile attributes](#custom-attributes).

#### Object

Objects can be used to group attributes. They cannot have more than 3 levels of nesting, both objects and array count as one level of nesting.

```json
{
  "events": [
    {
      "name": "validated_purchase",
      "attributes": {
        "delivery_address": {
          "number": 43,
          "street": "Rue Beaubourg",
          "zip_code": 75003,
          "city": "Paris",
          "country": "France"
        }
      }
    }
  ]
}
```

#### Array

You can set array of strings (max 300 characters) and array of objects. You cannot mix several attribute types in one array. Arrays can't be nested.

```json
{
  "events": [
    {
      "name": "validated_purchase",
      "attributes": {
        "items_list": [
          {
            "name": "Basic Tee",
            "size": "M",
            "price": 23.99,
            "url(item_url)": "https://batch-store.com/basic-tee",
            "url(item_image)": "https://batch-store.com/basic-tee/black/image.png",
            "in_sales": true
          },
          {
            "name": "Short socks pack x3",
            "size": "38-40",
            "price": 15.99,
            "url(item_url)": "https://batch-store.com/short-socks-pack-x3",
            "url(item_image)": "https://batch-store.com/short-socks-pack-x3/image.png",
            "in_sales": false
          }
        ],
        "metadata": [
          "first_purchase",
          "apple_pay"
        ]
      }
    }
  ]
}
```

#### Reserved event attributes

Some event attributes have reserved keys, and are all prefixed by a *$* sign. This is the list of currently reserved event attributes. You cannot set an event attribute starting by a *$* sign.

In the Trigger Events API, you were able to set a label and tags at the root of an event, with the limit of 1 label and 10 tags.

With the Profile API and the introduction of the array type in event attributes, you can set more that one array on profiles events. This is only supported for profiles, and not on the install centric data model, which currently powers push notification and In-App messages.

However, it's still possible to set a label and tags on events in the install centric data model by using $label and $tags, and activate compatibility flows. This way, you will be able to use this data for your push and in-app communications.

## Responses

### Success

If the POST to the API endpoint is successful you will receive an HTTP 202 confirmation. This means the API call was entirely valid and will be processed as requested.

```json
HTTP Status Code: 202

Response:
{
  "code": "SUCCESS"
}
```

### Success with partial errors

If your API calls has non fatal errors, you will receive an HTTP 202 confirmation and details about the non fatal errors. Data that isn't listed in the errors array will be processed.

```json
HTTP Status Code: 202

{
    "code": "SUCCESS_WITH_PARTIAL_ERRORS",
    "errors": [
        {
            "category": "attribute",
            "bulk_index": 0,
            "reason": "Array attributes must not contain empty string item",
            "attribute": "array"
        }
    ]
}
```

### Failure

If the POST data does not meet the API requirements you will receive an actionable error message. Contact us at <support@batch.com> if you need further support.

* `AUTHENTICATION_INVALID` (HTTP status code: 401, error\_code: 10)
* `ROUTE_NOT_FOUND` (HTTP status code: 404, error\_code: 20)
* `MISSING_PARAMETER` (HTTP status code: 400, error\_code: 30)
* `MALFORMED_PARAMETER` (HTTP status code: 400, error\_code: 31)
* `MALFORMED_JSON_BODY` (HTTP status code: 400, error\_code: 32)
* `SERVER_ERROR` (HTTP status code: 500, error\_code: 1)
* `MAINTENANCE_ERROR` (HTTP status code: 503, error\_code: 2)
* `TOO_MANY_REQUESTS` (HTTP status code: 429, error\_code: 60)

If you get a "too many requests" response, please wait for at least 5 seconds before trying again. Further requests might still return this error.

```json
{
    "error_code": "MALFORMED_PARAMETER",
    "error_message": "Invalid project key project_062ay7ywmgvqccwanj647mmqm1smq2k"
}
```

## Frequently asked questions

#### How can I see that the profile data is correctly updated during implementation?

The Profile view page of the dashboard displays profiles in real-time so you can review your implementation.

#### How can I use objects and arrays of objects in message personalization?

See the [Message personalization documentation.](https://doc.batch.com/getting-started/features/customer-engagement-platform/message/personalization)

#### Is the Profile API compatible with the installation based data model? Can I use the Profile API to send data and use it for push notifications and in-app messages?

Emails are sent on the base of profiles, whereas push notifications & In-App messages are sent on an app-based userbase made of installations.

By default, the Profile API only updates the profile base which is used to send email messages. However, compatibility flows are available to allow you to feed all user data with the Profile API, including the install centric userbase. Compatibility flows are set up by Batch team, reach out to <support@batch.com> for more information.

If you send more than 15 attributes in one event, only the first 15 event attributes (alphabetical order) will be written on the install centric userbase. If you would like to set a label and tags, use the compatibility attributes $label and $tags.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.batch.com/developer/api/cep/profiles/update.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
