> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.benchling.com/in-vivo/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.benchling.com/in-vivo/_mcp/server.

# Create measurements

POST https://acme-api.invivo.benchling.com/api/public/v1/studies/{studyId}/measurements
Content-Type: application/json

Records measurements for between 1 and 100 animal and day combinations on the study, computing each calculation from the variables supplied. The request is validated as a whole; if any item is invalid, none are created. A study that is not on your team is reported as a validation error rather than as forbidden.

Reference: https://docs.benchling.com/in-vivo/study-measurements/measurement-bulk-create-v-2

## Authentication

- OAuth2 — send the obtained token as `Authorization: Bearer <token>`

## Request

### Path parameters

- `studyId` (string, required) — The unique identifier of the study.

### Body (application/json)

This endpoint expects a list of MeasurementBulkCreateV2.

- `list of MeasurementBulkCreateV2`

## Response

### 201

The created measurements.

- `data` (list of MeasurementV2, required) — The measurements.

## Errors

### 400 Bad Request Error

The request was rejected. Validation failures return a `ValidationErrorResponse`; an unrecognised `X-Studies-Api-Version` header returns an `AuthErrorResponse` with the code `invalid_header`.

- `BadRequestErrorResponse`

### 401 Unauthorized Error

Authentication failed. The body is normally empty; when an otherwise valid access token is missing required claims the body is an `AuthErrorResponse` with the code `invalid_token`.

- `any`

### 403 Forbidden Error

The access token does not grant the scopes this operation requires. The body is normally empty; when the token's team is missing or deleted the body is an `AuthErrorResponse` with the code `invalid_data`.

- `any`

### 429 Too Many Requests Error

A rate limit was exceeded. Retry once the window resets; no `Retry-After` header is sent, so use `X-RateLimit-Reset`.

- `error` ("rate_limit_exceeded", required) — A machine-readable error code.
- `message` (string, required) — A human-readable explanation of the failure.

## Types

### MeasurementBulkCreateV2

The measurements to record for one animal on one day. Each animal may appear at most once per `measured_at` within a request, and a calculation already recorded for the animal on that day is rejected.

- `animal_id` (string, required) — The animal measured. Must be on the study.
- `measured_at` (date, required) — The day the measurements were taken.
- `calculations` (list of MeasurementCreateCalculationV2, required) — The calculations to record, each listed at most once. At least one variable must be supplied across them.

### MeasurementV2

A set of readings taken on an animal on a single day, together with any values derived from them. Which readings and calculations a measurement carries is determined by your team's measurement settings; a measurement that recorded a reading no longer configured for your team omits it.

- `id` (string, required) — Unique identifier for a measurement.
- `measured_at` (date, required, nullable) — The date the readings were taken, which may be earlier than `created_at` when they were recorded retrospectively.
- `calculations` (list of CalculationV2, required) — The values recorded and derived, one entry per configured calculation.
- `study` (StudyNestedV1, required, nullable) — The study the animal was on when the readings were taken. Null when the animal is not on a study.
- `animal` (AnimalNestedV1, required) — The animal a record belongs to, as embedded in dosages, measurements, observations and samples. Carries the same attributes as an animal retrieved directly, except that the cage, study and treatment group are given as identifiers rather than embedded objects.
- `user` (UserV1, required, nullable) — The user who recorded the readings.
- `created_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.
- `updated_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.

### ValidationErrorResponse

Returned when a request body or parameter fails validation.

- `message` (string, required) — A summary of the failure.
- `errors` (map from string to list of string, required) — Failure messages keyed by the offending field, using the field's name as it appears on the wire. Nested fields are dotted, e.g. `schedule.duration`.

### AuthErrorResponse

Returned when a request is rejected during authentication, before it is processed.

- `error` (enum, required) — A machine-readable error code.
  - Allowed values: `invalid_header`, `invalid_token`, `invalid_data`
- `error_description` (string, required) — A human-readable explanation of the failure.

### ProblemDetail

An RFC 9457 problem detail, returned when a request is rejected before it reaches a controller method — most commonly an unparseable or absent JSON body. Unlike the rest of this API, null members are omitted rather than serialised as `null`.

- `title` (string, required) — A short, human-readable summary of the problem type.
- `status` (integer, required) — The HTTP status code, repeated in the body.
- `detail` (string, required) — A human-readable explanation specific to this occurrence.
- `instance` (string, required) — The path of the request that produced the problem.

### MeasurementCreateCalculationV2

One calculation to record and the input variables to record for it. When the study's preset requires every input of a calculation, all of them must be supplied together.

- `key` (string, required) — The calculation, as configured on the study's measurement preset or one of the reserved variables such as `weight`.
- `variables` (list of MeasurementCreateCalculationVariableV2, required) — The input variables for the calculation. Each must be an input of this calculation, and a variable may be supplied only once per animal and day. Consumption variables are not accepted.

### CalculationV2

One value on a measurement, together with the readings it was derived from. A calculation is either a reading taken directly — in which case `formula` is just its own key and `variables` holds the single reading — or a value computed from several readings, in which case `variables` holds one entry per term in `formula`. Which calculations a measurement carries is determined by your team's measurement settings, not by the API.

- `id` (string, required, nullable) — The recorded value this calculation came from. Null for a derived calculation that has no stored value of its own.
- `key` (string, required, nullable) — The slug identifying the calculation in your team's measurement settings.
- `name` (string, required, nullable) — The display name of the calculation.
- `unit` (string, required, nullable) — The unit the calculation is expressed in.
- `excluded` (boolean, required) — Whether the value has been excluded from analysis. An excluded value is still returned; it is left out of aggregates and charts.
- `value` (string, required, nullable) — The calculated value, as a string so that the precision recorded is preserved exactly.
- `formula` (string, required, nullable) — The expression the value was computed from, written in terms of the `key` of each variable. A directly recorded reading has its own key as the formula.
- `variables` (list of VariableV2, required) — The readings the value was computed from.

### StudyNestedV1

The study a record belongs to, as embedded in animals, dosages, measurements, observations and samples. Carries the same attributes as a study retrieved directly, except that the project is given as an identifier rather than an embedded object.

- `id` (string, required) — Unique identifier for a study.
- `project_id` (string, required, nullable) — The project of the study. Null when the study is not in a project.
- `name` (string, required) — The name of the study.
- `code` (string, required, nullable) — The code of the study.
- `study_code_id` (string, required, nullable) — The unique identifier of the study code template.
- `type` (enum, required) — Whether the study is run in-house or by an external partner. The `all` value accepted by some filters is not a valid study type and is never returned.
  - Allowed values: `internal`, `external`
- `status` (enum, required) — Review status of a study.
  - Allowed values: `awaiting_approval`, `in_progress`, `rejected`
- `description` (string, required, nullable) — The description of the study.
- `timezone` (string, required, nullable) — The timezone of the study, as an IANA timezone identifier.
- `metadata` (map from string to any, required, nullable) — The metadata associated to the study.
- `author` (StudyAuthorUserV1, required, nullable) — The author of the study.
- `owner` (StudyAuthorUserV1, required, nullable) — The owner of the study.
- `started_on` (date, required) — Calendar date with no time or offset component, in ISO-8601 form.
- `tracking_day_base` (integer, required, nullable) — The day number assigned to the study's start date, from which all subsequent tracking days are counted.
- `completed_at` (datetime, required, nullable) — The completed timestamp of the study.
- `canceled_at` (datetime, required, nullable) — The canceled timestamp of the study.
- `created_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.
- `updated_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.

### AnimalNestedV1

The animal a record belongs to, as embedded in dosages, measurements, observations and samples. Carries the same attributes as an animal retrieved directly, except that the cage, study and treatment group are given as identifiers rather than embedded objects.

- `id` (string, required) — Unique identifier for an animal.
- `name` (string, required) — The name of the animal.
- `number` (integer, required) — The position of the animal within its cage.
- `catalog` (string, required, nullable) — The catalog number of the animal.
- `species` (string, required, nullable) — The species of the animal, derived from its strain. Null when the animal has no strain assigned.
- `strain` (string, required, nullable) — The strain of the animal.
- `sex` (enum, required, nullable) — The sex of the animal.
  - Allowed values: `m`, `f`, `u`
- `dob` (date, required, nullable) — The date of birth of the animal.
- `donor` (string, required, nullable) — The donor identifier of the animal.
- `ear` (string, required, nullable) — The ear marking identifying the animal.
- `tag` (string, required, nullable) — The tag identifying the animal.
- `tail` (string, required, nullable) — The tail marking identifying the animal.
- `cage_id` (string, required, nullable) — The cage the animal is housed in.
- `study_id` (string, required, nullable) — The study the animal is enrolled on. Null when it is not on a study.
- `study_group_id` (string, required, nullable) — The treatment group the animal is assigned to. Null when it is not on a study, or is on one but not yet grouped.
- `tracking_date` (date, required, nullable) — The date from which the animal's tracking days are counted.
- `disease_induction_date` (date, required, nullable) — The date the animal had a disease induced.
- `disease_survivor` (boolean, required, nullable) — Whether the animal survived the induced disease.
- `terminated_at` (date, required, nullable) — The date the animal was terminated. Null while the animal is alive.
- `terminated_by` (StudyAuthorUserV1, required, nullable) — The user who recorded the termination.
- `terminated_reason` (AnimalTerminatedReasonV1, required, nullable) — Why the animal was terminated.
- `metadata` (map from string to any, required, nullable) — The metadata associated to the animal.
- `created_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.
- `updated_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.

### UserV1

A member of your team, as referenced by the records they created. This is the fuller user representation; studies reference a narrower one that omits `status`, `metadata` and the timestamps.

- `id` (string, required) — Unique identifier for a user.
- `name` (string, required) — The name of the user.
- `email` (string, required) — The email address of the user.
- `timezone` (string, required, nullable) — The timezone of the user, as an IANA timezone identifier.
- `status` (enum, required) — The standing of a user within your team. `pending` means the invitation has not yet been accepted, and `revoked` means access was withdrawn.
  - Allowed values: `active`, `pending`, `inactive`, `revoked`
- `metadata` (map from string to any, required, nullable) — The metadata associated to the user.
- `created_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.
- `updated_at` (datetime, required) — UTC timestamp with microsecond precision, always suffixed with `Z`, in the form `yyyy-MM-ddTHH:mm:ss.SSSSSSZ`.

### MeasurementCreateCalculationVariableV2

One input variable value for a calculation.

- `key` (string, required) — The variable, as named on the calculation.
- `value` (string, required) — The recorded value, as a string so that its precision is preserved.

### VariableV2

One input to a calculation. A calculation that is a single recorded reading has one variable holding that reading; a derived calculation has one variable per reading its formula combines.

- `id` (string, required, nullable) — The reading the value was recorded against. Null when the reading is no longer configured for your team.
- `key` (string, required, nullable) — The slug identifying the reading in your team's measurement settings.
- `value` (string, required, nullable) — The recorded value, as a string so that the precision recorded is preserved exactly.
- `unit` (string, required, nullable) — The unit the value was recorded in.

### StudyAuthorUserV1

A user referenced as the author or owner of a study.

- `id` (string, required) — Unique identifier for a user.
- `name` (string, required) — The name of the user.
- `email` (string, required) — The email address of the user.
- `timezone` (string, required) — The timezone of the user. Defaults to `UTC` when the user has none set.

### AnimalTerminatedReasonV1

Why an animal was terminated. The reason is chosen from the shared glossary, or recorded free-form when no glossary entry fits.

- `glossary_id` (string, required, nullable) — The glossary entry the reason was chosen from. Null when the reason was recorded free-form.
- `title` (string, required, nullable) — The reason for the termination.
- `comment` (string, required, nullable) — Any further detail recorded alongside the reason.

## Examples

**Request**

```json
[
  {
    "animal_id": "aml_7KpQ2mNvR4tXwZ9bYcDfGh",
    "measured_at": "2026-09-18",
    "calculations": [
      {
        "key": "weight",
        "variables": [
          {
            "key": "weight",
            "value": "25.3"
          }
        ]
      }
    ]
  }
]
```

**Response**

```json
{
  "data": [
    {
      "id": "mst_6pR3kWqMvN9xTz5YbCdFgL",
      "measured_at": "2026-09-18",
      "calculations": [
        {
          "id": "vbl_4qQ6mKpNvR2zTw9YbCdFgX",
          "key": "weight",
          "name": "Weight",
          "unit": "g",
          "excluded": true,
          "value": "55",
          "formula": "weight",
          "variables": [
            {
              "id": "vbl_4qQ6mKpNvR2zTw9YbCdFgX",
              "key": "weight",
              "value": "55",
              "unit": "g"
            }
          ]
        }
      ],
      "study": {
        "id": "sdy_3vQ8kZpLmN2xRt7YwBcDfG",
        "project_id": "pjt_9kLmNpQrStUvWxYzAbCdEf",
        "name": "string",
        "code": "string",
        "study_code_id": "sdc_7hJ4nQrXsT9wYzAbCdEfGh",
        "type": "internal",
        "status": "in_progress",
        "description": "string",
        "timezone": "America/Los_Angeles",
        "metadata": {
          "cohort_size": 24,
          "species": "Mus musculus"
        },
        "author": {
          "id": "usr_2fBnKqWxYz5tRvMpLsJdHc",
          "name": "string",
          "email": "string",
          "timezone": "string"
        },
        "owner": {
          "id": "usr_2fBnKqWxYz5tRvMpLsJdHc",
          "name": "string",
          "email": "string",
          "timezone": "string"
        },
        "started_on": "2026-09-18",
        "tracking_day_base": 1,
        "completed_at": "2026-09-18T14:03:22.123456Z",
        "canceled_at": "2026-09-18T14:03:22.123456Z",
        "created_at": "2026-09-18T14:03:22.123456Z",
        "updated_at": "2026-09-18T14:03:22.123456Z"
      },
      "animal": {
        "id": "aml_7KpQ2mNvR4tXwZ9bYcDfGh",
        "name": "string",
        "number": 1,
        "catalog": "string",
        "species": "Mus musculus",
        "strain": "string",
        "sex": "m",
        "dob": "2026-09-18",
        "donor": "string",
        "ear": "string",
        "tag": "string",
        "tail": "string",
        "cage_id": "cge_5mR8kWpQnL3xTv7YzBcDfH",
        "study_id": "sdy_3vQ8kZpLmN2xRt7YwBcDfG",
        "study_group_id": "grp_9nT4kZpMvR2xQw8YbCdFgJ",
        "tracking_date": "2026-09-18",
        "disease_induction_date": "2026-09-18",
        "disease_survivor": true,
        "terminated_at": "2026-09-18",
        "terminated_by": {
          "id": "usr_2fBnKqWxYz5tRvMpLsJdHc",
          "name": "string",
          "email": "string",
          "timezone": "string"
        },
        "terminated_reason": {
          "glossary_id": "gly_5xQ4mKpNvR9zTw3YbCdFgV",
          "title": "string",
          "comment": "string"
        },
        "metadata": {
          "cohort_size": 24,
          "species": "Mus musculus"
        },
        "created_at": "2026-09-18T14:03:22.123456Z",
        "updated_at": "2026-09-18T14:03:22.123456Z"
      },
      "user": {
        "id": "usr_2fBnKqWxYz5tRvMpLsJdHc",
        "name": "string",
        "email": "string",
        "timezone": "America/Los_Angeles",
        "status": "active",
        "metadata": {
          "cohort_size": 24,
          "species": "Mus musculus"
        },
        "created_at": "2026-09-18T14:03:22.123456Z",
        "updated_at": "2026-09-18T14:03:22.123456Z"
      },
      "created_at": "2026-09-18T14:03:22.123456Z",
      "updated_at": "2026-09-18T14:03:22.123456Z"
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://acme-api.invivo.benchling.com/api/public/v1/studies/sdy_3vQ8kZpLmN2xRt7YwBcDfG/measurements"

payload = [
    {
        "animal_id": "aml_7KpQ2mNvR4tXwZ9bYcDfGh",
        "measured_at": "2026-09-18",
        "calculations": [
            {
                "key": "weight",
                "variables": [
                    {
                        "key": "weight",
                        "value": "25.3"
                    }
                ]
            }
        ]
    }
]
headers = {
    "Authorization": "<token>.",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://acme-api.invivo.benchling.com/api/public/v1/studies/sdy_3vQ8kZpLmN2xRt7YwBcDfG/measurements';
const options = {
  method: 'POST',
  headers: {Authorization: '<token>.', 'Content-Type': 'application/json'},
  body: '[{"animal_id":"aml_7KpQ2mNvR4tXwZ9bYcDfGh","measured_at":"2026-09-18","calculations":[{"key":"weight","variables":[{"key":"weight","value":"25.3"}]}]}]'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```