> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abconvert.io/llms.txt
> Use this file to discover all available pages before exploring further.

# API overview

> The ABConvert API is a REST API for creating tests, managing their lifecycle, and reading results.

The ABConvert API lets you run A/B tests on your Shopify store from your own code. Use it to:

* **Create and launch tests**: price, shipping, theme, template, URL redirect, checkout, and offer tests, with the same validation the ABConvert admin runs.
* **Automate the lifecycle**: schedule a window, preview each test group on your storefront, pause, resume, and end tests from your own tooling.
* **Read results**: outcomes with Bayesian and frequentist statistics, custom breakdowns by country, device, or UTM, and order-level CSV exports.

<Note>
  Endpoints marked beta can change. Webhooks ship in a later release. Until
  then, poll [`GET /v1/experiments`](/api-reference/experiments/list-tests)
  to detect state changes.
</Note>

## Base URL

```text theme={null}
https://api.abconvert.io/v1
```

Authenticate every request with a bearer token. See [Authentication](/api-reference/authentication).

## Terminology

The API's resource is the experiment: every endpoint, field, and identifier says `experiment`. Everywhere outside code, these docs say "test," the same word as the ABConvert admin. The two names refer to the same object.

## Identifiers

| Resource | Identifier |
| - | - |
| Shopify products, product variants, markets, delivery zones, shipping rates, and themes | The Shopify GID, in requests and responses: `gid://shopify/Product/8123456789` |
| Tests | The numeric ID shown in the ABConvert admin, as a string: `"3021"` |
| Test groups | The group's index in `test_groups`, counting from 0 |
| Export jobs | An opaque string |

A test splits traffic between test groups, and the index is a test group's identity: results, preview URLs, and `PATCH` merges all reference it. `name` is only a display label, and you can change it at any time.

## Data formats

| Value | Format |
| - | - |
| Money | An object, never a bare number: `{"amount": "17.99", "currency": "USD"}`. Amounts are decimal strings. |
| Percentages | Integers. Splits run 0 to 100; `traffic_allocation` runs 1 to 100. |
| Timestamps | ISO 8601, in fields suffixed `_at`. |

## Requests and responses

**Updates are sparse.** `PATCH` changes only the fields you send. Leave a field out and it stays unchanged. An array whose entries carry an identifier merges entry by entry instead of being replaced: `prices` merges on `product_variant_id`, `test_groups` on `index`. To delete one entry, send it with `"remove": true`.

**Lists are cursor-paginated.** Every list response uses the same envelope:

```json theme={null}
{ "object": "list", "data": [], "has_more": true, "next_cursor": "..." }
```

To fetch the next page, pass the previous page's `next_cursor` as `?cursor=`. Cursors are opaque: store and replay the returned value unchanged; do not parse it or construct your own.

## Compatibility

The `/v1` contract evolves additively. New response fields, enum values, and optional capabilities may appear without a version change and are not breaking changes; clients must ignore response fields they do not recognize. We announce breaking changes in the API changelog and merchant release notes, with a migration window before they take effect.

**Errors share one shape.** Every status code returns the same error object, carrying a stable machine-readable `code`:

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "unknown_parameter",
    "message": "Unknown parameter: hypothesys.",
    "param": "hypothesys"
  }
}
```

A success response can also carry non-blocking findings, in a top-level `warnings` array.

**Retries are safe on create.** `POST /v1/experiments` and `POST /v1/experiments/{id}/exports` accept an `Idempotency-Key` header, so a retried request never creates a duplicate.

## Test lifecycle

A test holds one of seven statuses: `draft`, `preview`, `active`, `paused`, `ended`, `failed`, or `archived`.

Creating a test always produces a `draft`. `start` is the only way to launch one. Every lifecycle action applies immediately and returns the updated test.

Launching locks the test's configuration. After launch, you can still change:

* `name` and `hypothesis`
* `primary_metric`
* Each test group's `name` and `split`
* The schedule's `end_at`

Changing anything else requires a draft.

<Tip>
  Try it:

  1. Create a draft with [Create a test](/api-reference/experiments/create-a-test).
  2. [Preview a test](/api-reference/lifecycle/preview-a-test). The response returns one preview URL per test group.
  3. Open a preview URL in your browser to see that test group live on your storefront.
  4. [Launch a test](/api-reference/lifecycle/launch-a-test).
</Tip>

## Feature availability

Every ABConvert plan includes API access. Individual test types check your shop's entitlements twice: when you create the test, and when you launch it. A shop that fails a check gets a 403 `permission_error` whose `code` and `message` name exactly what is missing.

| Test type | Requirement |
| - | - |
| Price | An ABConvert plan that includes price testing |
| Price, with `markets` | The SCALE plan tier or above |
| Offer | The new ABConvert version |
| Shipping | Carrier-calculated shipping on your Shopify plan |
| Checkout, theme, template, URL redirect | None. Available on every plan. |

A lapsed subscription (`subscription_inactive`) or a reached usage cap (`billing_cap_reached`) blocks create, `start`, and `resume`. On an annual plan, using all of the month's included, banked and purchased orders (`allowance_exhausted`) blocks `start` and `resume` until you add usage, upgrade, or the next month begins.

You keep control of tests that are already live: `pause`, `end`, and `archive` never check entitlements.

## Rate limits

Each token gets:

| Request class | Limit |
| - | - |
| Reads | 60 per minute |
| Writes | 10 per minute |
| Derivations | 10 per minute |

`GET` requests are reads. `POST /v1/experiments/{id}/results` is a derivation because it computes analytics without changing a test. All other non-`GET` requests spend the write budget, including `POST /v1/experiments/{id}/exports`: export creation is read-scoped for authorization, but write-classed for rate limiting and audit logging.

Every response carries `X-RateLimit-*` headers. A 429 response adds `Retry-After`, in seconds.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.