---
title: Run a multi-armed bandit optimization
slug: feature-experimentation/docs/run-a-mab-optimization
docTags: 
createdAt: 2026-08-05T20:39:55.000Z
---

A [multi-armed bandit (MAB) optimization](https://www.optimizely.com/optimization-glossary/multi-armed-bandit/) uses reinforcement learning to dynamically shift traffic toward better-performing variations. Unlike [A/B tests](docId:8pnfCv-sR37goLcxTtCPv), which split traffic evenly and wait for statistical significance, MABs continuously reallocate based on observed conversion rates.

:::hint{type="warning"}
**Important**
MAB optimizations do not produce statistical significance. The algorithm optimizes for total conversions by shifting traffic to winning variations, without analyzing why a variation performs better or worse.
:::

See [Run a multi-armed bandit optimization in Feature Experimentation](docId\:F5-ZUIoxCu2_XRy26tO-b) in the user documentation for additional information and use cases.

## Prerequisites

To configure a MAB, complete the following:

1. [Create a flag](docId\:eH-UMm_rOn_GdGDGLV6__) in your Feature Experimentation project.
2. [Handle user IDs](docId\:j0PPoMJWYbz04K76EKLUI).

:::hint{type="info"}
**Note**
You should configure a [user profile service](docId:0mbVhcaiWJm6M96ZohwI-) to ensure consistent user bucketing if you are using a server-side SDK.
:::

3. If you have not done so yet, [implement the Optimizely Feature Experimentation SDK's  Decide  method](docId\:JaXCjRbh8BbYMGgmsuTvr) in your application's codebase through a [flag](docId\:eH-UMm_rOn_GdGDGLV6__).

## Configure an MAB optimization in your Feature Experimentation project

### With the Feature Experimentation REST API

Use the [Update the Ruleset for a Flag in an Environment](docId\:wpUSoZjCEhG6L1zPVvfhf) endpoint to add a rule with `type` set to `multi_armed_bandit`.

:::hint{type="warning"}
**Important**
Before sending a `PATCH`, [\`GET the current ruleset\`](docId\:TxxX0KcGkrO_cG331nADs) and merge in only the fields you want to change. This prevents accidentally overwriting other settings.
:::

**Endpoint** – `PATCH https://api.optimizely.com/flags/v1/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset`

**Authentication** – Include your API key in the `Authorization` header as a Bearer token. See [Generate tokens and use the REST APIs](docId\:pQfa5KPTbmy6crey-nsrd).

**Request example**

```curl
curl --request PATCH \
     --url https://api.optimizely.com/flags/v1/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset \
     --header 'accept: application/json' \
     --header 'authorization: Bearer TOKEN' \
     --header 'content-type: application/json-patch+json' \
     --data '
[
  {
    "op": "add",
    "value": {
            "key": "mab_test",
            "name": "Multi-armed Test",
            "type": "multi_armed_bandit",
            "distribution_mode": "manual",
            "percentage_included": 10000,
            "metrics": [
                {
                    "aggregator": "unique",
                    "display_title": "add_to_cart",
                    "event_id": EVENT_ID,
                    "event_type": "custom",
                    "scope": "visitor",
                    "winning_direction": "increasing"
                }
            ],
            "variations": {
                "off": {
                    "key": "off",
                    "name": "Off",
                    "percentage_included": 3333
                },
                "on": {
                    "key": "on",
                    "name": "On",
                    "percentage_included": 3333
                },
                "on_hide_amounts": {
                    "key": "on_hide_amounts",
                    "name": "On Hide Amounts",
                    "percentage_included": 3334
                }
            },
            "audience_conditions": []
        },
    "path": "/rules/mab_test"
  }
]
'
```

**Response example**

```json
{
  "url": "/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset",
  "update_url": "/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset",
  "enable_url": "/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset/enabled",
  "rules": {
    "already-exisiting-a-b-test": {
      "key": "already-exisiting-a-b-test",
      "name": "Already-exisiting-a-b-test",
      "description": "",
      "variations": {
        "off": {
          "key": "off",
          "name": "Off",
          "percentage_included": 5000,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on": {
          "key": "on",
          "name": "On",
          "percentage_included": 5000,
          "variation_id": VARIATION_ID,
          "id": ID
        }
      },
      "baseline_variation_id": VARIATION_ID,
      "type": "a/b",
      "distribution_mode": "manual",
      "id": ID,
      "urn": "rules.flags.optimizely.com::ID",
      "archived": false,
      "enabled": false,
      "created_time": "2025-09-12T16:44:11.824135Z",
      "updated_time": "2025-09-12T17:03:20.001335Z",
      "audience_conditions": [
        "or",
        {
          "audience_id": AUDIENCE_ID
        }
      ],
      "audience_ids": [
        AUDIENCE_ID
      ],
      "percentage_included": 10000,
      "metrics": [
        {
          "event_id": EVENT_ID,
          "event_type": "pageview",
          "scope": "visitor",
          "aggregator": "unique",
          "winning_direction": "increasing",
          "display_title": "PDP Page Views",
          "alias": "b20520c09877f956ad8a7ef7723139006f01174e69b253d1896c9d0a82ebe212",
          "id": "2f7cd919-c73b-4a99-81c7-daa69e1cc2a6",
          "visibility": "account",
          "updated_at": "2025-05-21T15:40:30.967744070Z",
          "account_id": ACCOUNT_ID,
          "event_scoped": false
        }
      ],
      "allow_list": {},
      "group_rule": {
        "group_id": 0,
        "traffic_allocation": 0
      },
      "group_remaining_traffic_allocation": 100,
      "layer_id": 9300001984917,
      "layer_experiment_id": 9300002730629,
      "status": "draft"
    },
    "mab_test": {
      "key": "mab_test",
      "name": "Multi-armed Test",
      "description": "",
      "variations": {
        "off": {
          "key": "off",
          "name": "Off",
          "percentage_included": 3333,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on": {
          "key": "on",
          "name": "On",
          "percentage_included": 3333,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on_hide_amounts": {
          "key": "on_hide_amounts",
          "name": "On Hide Amounts",
          "percentage_included": 3334,
          "variation_id": VARIATION_ID,
          "id": ID
        }
      },
      "baseline_variation_id": null,
      "type": "multi_armed_bandit",
      "id": ID,
      "urn": "rules.flags.optimizely.com::ID",
      "archived": false,
      "enabled": false,
      "created_time": "2025-09-12T17:45:13.103056Z",
      "updated_time": "2025-09-12T17:45:13.072498Z",
      "audience_conditions": [],
      "audience_ids": [],
      "percentage_included": 10000,
      "metrics": [
        {
          "event_id": EVENT_ID,
          "event_type": "custom",
          "scope": "visitor",
          "aggregator": "unique",
          "winning_direction": "increasing",
          "display_title": "add_to_cart",
          "alias": "ALIAS_ID",
          "id": "ID",
          "project_id": PROJECT_ID,
          "visibility": "experiment",
          "updated_at": "2025-09-12T17:45:14.341649449Z",
          "account_id": ACCOUNT_ID,
          "event_scoped": false
        }
      ],
      "allow_list": {},
      "group_rule": {
        "group_id": 0,
        "traffic_allocation": 0
      },
      "group_remaining_traffic_allocation": 100,
      "layer_id": LAYER_ID,
      "layer_experiment_id": EXPERIMENT_LAYER_ID,
      "status": "draft"
    }
  },
  "rule_priorities": [
    "already-exisiting-a-b-test",
    "mab_test"
  ],
  "id": ID,
  "urn": "rulesets.flags.optimizely.com::ID",
  "archived": false,
  "enabled": false,
  "updated_time": "2025-09-12T17:45:13.443788Z",
  "flag_key": "doc-test-flag",
  "environment_key": "development",
  "environment_name": "Development",
  "environment_id": ENVIRONMENT_ID,
  "default_variation_key": "off",
  "default_variation_name": "Off",
  "revision": 9,
  "status": "draft",
  "role": "admin"
}
```

See [Update the Ruleset for a Flag in an Environment](docId\:wpUSoZjCEhG6L1zPVvfhf) for the full API reference.

### With the Feature Experimentation UI

See [Create an MAB optimization](docId\:F5-ZUIoxCu2_XRy26tO-b) in the Optimizely app documentation.

## Run the multi-armed bandit rule

Start your multi-armed bandit rule and flag (if it is not already running).

### With the Feature Experimentation REST API

1. Enable the flag by using the [Enable the Ruleset for a Flag in an Environment](docId:3EqUxeVRs0Ew1Pbe6yCII) endpoint.
   - **Endpoint** – `POST https://api.optimizely.com/flags/v1/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset/enabled`
   - **Authentication** – Include your API key in the `Authorization` header as a Bearer token. See [Generate tokens and use the REST APIs](docId\:pQfa5KPTbmy6crey-nsrd).
   - **Request example**

```curl
curl --request POST \
     --url https://api.optimizely.com/flags/v1/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset/enabled \
     --header 'accept: application/json' \
     --header 'authorization: Bearer TOKEN'
```

:::Paragraph{listStyleType="disc" indent="2"}
**Response example**
:::

```json
{
  "url": "/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset",
  "update_url": "/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset",
  "disable_url": "/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset/disabled",
  "rules": {
    "already-exisiting-a-b-test": {
      "key": "already-exisiting-a-b-test",
      "name": "Already-exisiting-a-b-test",
      "description": "",
      "variations": {
        "off": {
          "key": "off",
          "name": "Off",
          "percentage_included": 5000,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on": {
          "key": "on",
          "name": "On",
          "percentage_included": 5000,
          "variation_id": VARIATION_ID,
          "id": ID
        }
      },
      "baseline_variation_id": VARIATION_ID,
      "type": "a/b",
      "distribution_mode": "manual",
      "id": ID,
      "urn": "rules.flags.optimizely.com::ID",
      "archived": false,
      "enabled": false,
      "created_time": "2025-09-12T16:44:11.824135Z",
      "updated_time": "2025-09-12T19:40:45.033536Z",
      "audience_conditions": [
        "or",
        {
          "audience_id": AUDIENCE_ID
        }
      ],
      "audience_ids": [
        AUDIENCE_ID
      ],
      "percentage_included": 10000,
      "metrics": [
        {
          "event_id": EVENT_ID,
          "event_type": "pageview",
          "scope": "visitor",
          "aggregator": "unique",
          "winning_direction": "increasing",
          "display_title": "PDP Page Views",
          "alias": "ALIAS_ID",
          "id": "ID",
          "visibility": "account",
          "updated_at": "2025-05-21T15:40:30.967744070Z",
          "account_id": ACCOUNT_ID,
          "event_scoped": false
        }
      ],
      "allow_list": {},
      "group_rule": {
        "group_id": 0,
        "traffic_allocation": 0
      },
      "group_remaining_traffic_allocation": 100,
      "layer_id": LAYER_ID,
      "layer_experiment_id": LAYER_EXPERIMENT-ID,
      "status": "draft"
    },
    "mab_test": {
      "key": "mab_test",
      "name": "Multi-armed Test",
      "description": "",
      "variations": {
        "off": {
          "key": "off",
          "name": "Off",
          "percentage_included": 3333,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on": {
          "key": "on",
          "name": "On",
          "percentage_included": 3333,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on_hide_amounts": {
          "key": "on_hide_amounts",
          "name": "On Hide Amounts",
          "percentage_included": 3334,
          "variation_id": VARIATION_ID,
          "id": ID
        }
      },
      "baseline_variation_id": null,
      "type": "multi_armed_bandit",
      "id": ID,
      "urn": "rules.flags.optimizely.com::ID",
      "archived": false,
      "enabled": false,
      "created_time": "2025-09-12T17:45:13.103056Z",
      "updated_time": "2025-09-12T19:40:45.033536Z",
      "audience_conditions": [],
      "audience_ids": [],
      "percentage_included": 10000,
      "metrics": [
        {
          "event_id": EVENT_ID,
          "event_type": "custom",
          "scope": "visitor",
          "aggregator": "unique",
          "winning_direction": "increasing",
          "display_title": "add_to_cart",
          "alias": "ALIAS_ID",
          "id": "ID",
          "project_id": PROJECT_ID,
          "visibility": "experiment",
          "updated_at": "2025-09-12T17:45:14.341649449Z",
          "account_id": ACCOUNT_ID,
          "event_scoped": false
        }
      ],
      "allow_list": {},
      "group_rule": {
        "group_id": 0,
        "traffic_allocation": 0
      },
      "group_remaining_traffic_allocation": 100,
      "layer_id": LAYER_ID,
      "layer_experiment_id": EXPERIMENT_ID,
      "status": "draft"
    }
  },
  "rule_priorities": [
    "already-exisiting-a-b-test"
  ],
  "id": ID,
  "urn": "rulesets.flags.optimizely.com::ID",
  "archived": false,
  "enabled": true,
  "updated_time": "2025-09-12T19:40:45.033536Z",
  "flag_key": "doc-test-flag",
  "environment_key": "development",
  "environment_name": "Development",
  "environment_id": ENVIRONMENT_ID,
  "default_variation_key": "off",
  "default_variation_name": "Off",
  "revision": 10,
  "status": "running",
  "role": "admin"
}
```

2. Launch your multi-armed bandit rule by enabling the rule. Use the [Update the Ruleset for a Flag in an Environment](docId\:wpUSoZjCEhG6L1zPVvfhf) endpoint and setting the `enabled` path to `true`.

:::hint{type="warning"}
**Important**
Before sending a `PATCH`, [\`GET the current ruleset\`](docId\:TxxX0KcGkrO_cG331nADs) and merge in only the fields you want to change. This prevents accidentally overwriting other settings.
:::

:::Paragraph{listStyleType="disc" indent="2"}
**Endpoint** – `PATCH https://api.optimizely.com/flags/v1/projects/PROJECT_ID/flags/FLAG_KEY/environments/ENVIRONMENT_KEY/ruleset`
:::

:::Paragraph{listStyleType="disc" indent="2"}
**Authentication** – Include your API key in the `Authorization` header as a Bearer token. See [Generate tokens and use the REST APIs](docId\:pQfa5KPTbmy6crey-nsrd).
:::

:::Paragraph{listStyleType="disc" indent="2"}
**Request example**
:::

```curl
 curl --request PATCH \
     --url https://api.optimizely.com/flags/v1/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset \
     --header 'accept: application/json' \
     --header 'authorization: Bearer TOKEN' \
     --header 'content-type: application/json-patch+json' \
     --data '
[
   {
      "op": "replace",
      "path": "/rules/mab-test/enabled",
      "value": true
   }
]
'
```

:::Paragraph{listStyleType="disc" indent="2"}
**Response example**
:::

```json
{
  "url": "/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset",
  "update_url": "/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset",
  "disable_url": "/projects/PROJECT_ID/flags/doc-test-flag/environments/development/ruleset/disabled",
  "rules": {
    "already-exisiting-a-b-test": {
      "key": "already-exisiting-a-b-test",
      "name": "Already-exisiting-a-b-test",
      "description": "",
      "variations": {
        "off": {
          "key": "off",
          "name": "Off",
          "percentage_included": 5000,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on": {
          "key": "on",
          "name": "On",
          "percentage_included": 5000,
          "variation_id": VARIATION_ID,
          "id": ID
        }
      },
      "baseline_variation_id": BASELINE_VARIATION_ID,
      "type": "a/b",
      "distribution_mode": "manual",
      "id": ID,
      "urn": "rules.flags.optimizely.com::ID",
      "archived": false,
      "enabled": false,
      "created_time": "2025-09-12T16:44:11.824135Z",
      "updated_time": "2025-09-12T19:51:52.213225Z",
      "audience_conditions": [
        "or",
        {
          "audience_id": AUDIENCE_ID
        }
      ],
      "audience_ids": [
        AUDIENCE_ID
      ],
      "percentage_included": 10000,
      "metrics": [
        {
          "event_id": EVENT_ID,
          "event_type": "pageview",
          "scope": "visitor",
          "aggregator": "unique",
          "winning_direction": "increasing",
          "display_title": "PDP Page Views",
          "alias": "b20520c09877f956ad8a7ef7723139006f01174e69b253d1896c9d0a82ebe212",
          "id": "2f7cd919-c73b-4a99-81c7-daa69e1cc2a6",
          "visibility": "account",
          "updated_at": "2025-05-21T15:40:30.967744070Z",
          "account_id": ACCOUNT_ID,
          "event_scoped": false
        }
      ],
      "fetch_results_ui_url": "https://app.optimizely.com/v2/projects/PROJECT_ID/results/RESULT_ID/experiments/EXPERIMENT_ID?baseline=1500745",
      "allow_list": {},
      "group_rule": {
        "group_id": 0,
        "traffic_allocation": 0
      },
      "group_remaining_traffic_allocation": 100,
      "layer_id": LAYER_ID,
      "layer_experiment_id": LAYER_EXPERIMENT_ID,
      "status": "running"
    },
    "mab_test": {
      "key": "mab_test",
      "name": "Multi-armed Test",
      "description": "",
      "variations": {
        "off": {
          "key": "off",
          "name": "Off",
          "percentage_included": 3333,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on": {
          "key": "on",
          "name": "On",
          "percentage_included": 3333,
          "variation_id": VARIATION_ID,
          "id": ID
        },
        "on_hide_amounts": {
          "key": "on_hide_amounts",
          "name": "On Hide Amounts",
          "percentage_included": 3334,
          "variation_id": VARIATION_ID,
          "id": ID
        }
      },
      "baseline_variation_id": null,
      "type": "multi_armed_bandit",
      "id": ID,
      "urn": "rules.flags.optimizely.com::ID",
      "archived": false,
      "enabled": true,
      "created_time": "2025-09-12T17:45:13.103056Z",
      "updated_time": "2025-09-12T19:40:45.033536Z",
      "audience_conditions": [],
      "audience_ids": [],
      "percentage_included": 10000,
      "metrics": [
        {
          "event_id": EVENT_ID,
          "event_type": "custom",
          "scope": "visitor",
          "aggregator": "unique",
          "winning_direction": "increasing",
          "display_title": "add_to_cart",
          "alias": "ALIAS_ID",
          "id": "ID",
          "project_id": PROJECT_ID,
          "visibility": "experiment",
          "updated_at": "2025-09-12T17:45:14.341649449Z",
          "account_id": ACCOUNT-ID,
          "event_scoped": false
        }
      ],
      "allow_list": {},
      "group_rule": {
        "group_id": 0,
        "traffic_allocation": 0
      },
      "group_remaining_traffic_allocation": 100,
      "layer_id": LAYER_ID,
      "layer_experiment_id": LAYER_EXPERIMENT_ID,
      "status": "draft"
    }
  },
  "rule_priorities": [
    "already-exisiting-a-b-test",
    "mab_test"
  ],
  "id": ID,
  "urn": "rulesets.flags.optimizely.com::ID",
  "archived": false,
  "enabled": true,
  "updated_time": "2025-09-12T19:51:52.287147Z",
  "flag_key": "doc-test-flag",
  "environment_key": "development",
  "environment_name": "Development",
  "environment_id": ENVIRONMENT_ID,
  "default_variation_key": "off",
  "default_variation_name": "Off",
  "revision": 11,
  "status": "running",
  "role": "admin"
}
```

### With the Feature Experimentation UI

See [Run a multi-armed bandit optimization in Feature Experimentation](docId\:F5-ZUIoxCu2_XRy26tO-b) in the user documentation.

## Implement the multi-armed bandit

If you have already [implemented the flag](docId\:eH-UMm_rOn_GdGDGLV6__) in your application, no further changes are needed. If not, add the `Decide` call to your code:

```swift
// Decide if user sees a feature flag variation
let user = optimizely.createUserContext(userId: "user123", attributes: ["logged_in":true])
let decision = user.decide(key: "flag_1")
let enabled = decision.enabled
```

```go
// Decide if user sees a feature flag variation
user := optimizely.CreateUserContext("user123", map[string]interface{}{"logged_in": true})
decision := user.Decide("flag_1", nil)
enabled := decision.Enabled
```

```python
# Decide if user sees a feature flag variation
user = optimizely.create_user_context("user123", {'logged_in': True})
decision = user.decide("flag_1")
enabled = decision.enabled
```

```php
// Decide if user sees a feature flag variation
$user = $optimizely_client->createUserContext('user123', ['logged_in' => true]);
$decision = $user->decide('flag_1');
$enabled = $decision->getEnabled();
```

```ruby
# Decide if user sees a feature flag variation
user = optimizely_client.create_user_context('user123', {'logged_in' => true})
decision = user.decide('flag_1')
enabled = decision.enabled
```

```csharp
// Decide if user sees a feature flag variation
var user = optimizely.CreateUserContext("user123", new UserAttributes { { "logged_in", true } });
var decision = user.Decide("flag_1");
var enabled = decision.Enabled;
```

```java
// Decide if user sees a feature flag variation
OptimizelyUserContext user = optimizely.createUserContext("user123", new HashMap<String, Object>() { { put("logged_in", true); } });
OptimizelyDecision decision = user.decide("flag_1");
Boolean enabled = decision.getEnabled();
```

```javascript
// Decide if user sees a feature flag variation
const user = optimizely.createUserContext('user123', { logged_in: true });
const decision = user.decide('flag_1');
const enabled = decision.enabled;
```

```javascript
// Decide if user sees a feature flag variation
const [decision] = useDecision('flag_1', { overrideUserAttributes: { logged_in: true }});
const enabled = decision.enabled;
```

```dart
// Decide if user sees a feature flag variation
var user = await flutterSDK.createUserContext(userId: "user123");
var decisionResponse = await user!.decide("flag_1");
var decision = decisionResponse.decision;
var enabled = decision!.enabled;
```

See the following for more detailed examples.

- [Android example usage](docId\:nQkw1CJn6rhkkZcHUhWlu)
- [Go example usage](docId\:BtqVa7hPVV3lakICHOePE)
- [C# example usage](docId\:TcOrbpenEwK6XfJSXEWhQ)
- [Flutter example usage](docId\:aZh7xqKhZ9b8I15ukdQnS)
- [Java example usage](docId\:oczD_Ka_bhgCygo60xG31)
- [JavaScript example usage](docId\:Hd7ZwchAg70iG5S_5kgpM) – SDK versions 6.0.0 and later.
- [JavaScript (Browser) example usage](docId\:c8V_lO-12gUfsBNUuczJI) – SDK versions 5.3.5 and earlier.
- [JavaScript (Node) example usage](docId:0lhn1UwOiZajgk6h-cq2a) – SDK versions 5.3.5 and earlier.
- [PHP example usage](docId\:QDWBJTJc3c2qnd5EhAPQ9)
- [Python example usage](docId:0Afx9oaNuXQo-439j2JzL)
- [React example usage](docId:2XkPexPChtoDBKgmaMEEm)
- [Ruby example usage](docId\:Oh9NVDFfn7X-s1WTLfPUY)
- [Swift example usage](docId:3ZVLXuKme4kiqdO2Yt_H1)

`Decide` determines whether a user qualifies for a rule and which variation they receive. You can reuse the same flag implementation across different flag rules.

Users are evaluated against rules in the ruleset in order. See [Create feature flags](docId\:Z0Yy8O0N7GakXKfCPYtSi).
