---
title: "POST /v1/jobs/{jobId}/fit-curves"
description: Fit dose-response curves for a job
full: true
---

**Auth:** required. Send an `ivy_` API key (`Authorization: Bearer ivy_…` or `X-Ivy-Api-Key`), a Clerk session JWT (`Authorization: Bearer …`) or the Clerk `__session` cookie (browser sessions). See [Authentication](/docs/api/authentication).

Fits 4PL or 5PL dose-response curves to the plate readouts of a job, one fit per compound label, and returns them. The readouts of the chosen `channel` are optionally normalized first (`normalize`). With `model` set to `auto`, each compound gets whichever of 4PL and 5PL the fit statistics prefer.

By default nothing is stored. With `persist` set to `true` the fits are also saved for the job, which needs write access to it, and the job's rollups are refreshed on a best-effort basis. Persisting failures are not reported: the response is still 200. Without `persist` read access is enough.

The response carries `X-Elapsed-Ms` and `X-Model-Mode` headers. Requests are rate limited per user: 3 per second, 8 per 3 seconds and 8 per minute. With a project-scoped API key, a job outside the key's projects returns 404.

## Request

#### Path parameters

- `jobId` (string (uuid)) —
Job UUID.

#### Request body

- `channel` (string) —
Readout channel to fit

- `normalize` (enum (&quot;none&quot; | &quot;percent_control&quot; | &quot;percent_inhibition&quot;)) —

- `model` (enum (&quot;4PL&quot; | &quot;5PL&quot; | &quot;auto&quot;)) —
`auto` picks 4PL or 5PL per compound from the fit statistics

- `persist` (boolean) —
Also store the fits for the job; needs write access to it

##### Example

```json
{
  "channel": "primary",
  "normalize": "percent_inhibition",
  "model": "auto",
  "persist": false
}
```

## Responses

### `200` OK

The fitted curves

Response shape:

- `success` (enum (true)) —

- `channel` (string) —

- `normalize` (enum (&quot;none&quot; | &quot;percent_control&quot; | &quot;percent_inhibition&quot;)) —

- `results` (object[]) —

### `400` Bad Request

The job id is empty

Response shape: `object`

### `401` Unauthorized

Authentication required

Response shape: `object`

### `404` Not Found

The job does not exist, you cannot reach it with the access this request needs, or it is outside your API key's project scope. All of these return an error body with `NOT_FOUND`.

Response shape: `object`

### `405`

The method is not `POST` or `PATCH`; the `Allow` header says `POST, PATCH`

Response shape: `object`

### `429` Too Many Requests

Too many requests; `Retry-After` says how long to wait

Response shape: `object`

### `500` Internal Server Error

Fitting failed

Response shape: `object`

**curl**

```bash
curl -X POST 'https://ivybiosciences.com/api/v1/jobs/{jobId}/fit-curves' \
  -H "Authorization: Bearer $IVY_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "channel": "primary",
  "normalize": "percent_inhibition",
  "model": "auto",
  "persist": false
}'
```

**JavaScript**

```js
const res = await fetch('https://ivybiosciences.com/api/v1/jobs/{jobId}/fit-curves', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.IVY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
  "channel": "primary",
  "normalize": "percent_inhibition",
  "model": "auto",
  "persist": false
}),
});
const data = await res.json();
```

**Python**

```python
import os, requests

res = requests.post(
    "https://ivybiosciences.com/api/v1/jobs/{jobId}/fit-curves",
    headers={"Authorization": f"Bearer {os.environ['IVY_API_KEY']}"},
    json={
  "channel": "primary",
  "normalize": "percent_inhibition",
  "model": "auto",
  "persist": False
},
)
data = res.json()
```
