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

# Append Datapoints to a Dataset

POST https://api.prolific.com/api/v1/data-collection/datasets/{dataset_id}/datapoints
Content-Type: application/x-ndjson

Synchronously appends one or more JSONL records to an existing **V4 dataset**. Unlike the
file-upload flow, this endpoint processes the whole payload within the request and returns the
accepted/rejected counts directly — there is no async import job to poll.

**Request body:** JSONL — one JSON object per line — sent with a JSONL content type
(e.g. `application/x-ndjson`). Blank lines are skipped. CSV is not supported on this endpoint.

Each record is validated against the dataset's current schema. Valid records are persisted as
datapoints and sort after every existing datapoint. Records that are malformed or violate the
schema are rejected individually and reported in `errors` — they do not fail the whole request.
Re-appending an identical record is idempotent (counted as accepted, not written twice).

**Limits and preconditions:**
- At most 1000 records per request. Larger imports should use the file-upload flow.
- The dataset must be V4 and have a schema defined.
- Rejected with `409 Conflict` while a dataset import or a schema migration is in progress.

If the dataset is attached to any batches with `auto_sync_enabled`, a sync is triggered
automatically for the newly written datapoints.

Reference: https://docs.prolific.com/api-reference/task-builder/append-dataset-datapoints

## Authentication

- `Authorization` header (required) (prefixed with ` Token  `) — The Prolific API uses API token to authenticate requests. You can create an API token directly from your settings. Your API token does not have an expiry date and carries full permission, so be sure to keep them secure. If your token is leaked, delete it and create a new one directly in the app. In your requests add `Authorization` header with the value `Token <your token>`.

## Request

### Path parameters

- `dataset_id` (string, required) — The unique identifier of the V4 dataset to append to

### Body (application/x-ndjson)

This endpoint expects a string.

- `string`

## Response

### 200

Append processed. Returns per-request accepted/rejected counts. A `200` is returned even when some (or all) records were rejected — inspect `rejected` and `errors`.

- `accepted` (integer, required) — Number of records that passed validation (newly written or idempotent duplicates).
- `rejected` (integer, required) — Number of records rejected. Equal to the length of `errors`.
- `errors` (list of DatasetAppendError, required) — Record-level rejections. Empty when every record was accepted.

## Errors

### 400 Bad Request Error

Bad request — missing body, CSV payload, empty JSONL, too many records, a non-V4 dataset, or a dataset with no schema.

- `error` (ErrorDetail, required)

### 403 Forbidden Error

Forbidden — the user does not have access to the dataset's workspace.

- `any`

### 404 Not Found Error

Dataset not found

- `error` (ErrorDetail, required)

### 409 Conflict Error

Conflict — a dataset import or a schema migration is in progress. Retry once it completes.

- `error` (ErrorDetail, required)

## Types

### DatasetAppendError

A record-level validation error from a synchronous JSONL append request.

- `record_index` (integer, required) — One-based index of the rejected non-blank JSONL record in the append request body.
- `field` (string, required) — The schema field key that caused the rejection. The special value `_raw` indicates a whole-record parse failure (for example malformed JSON) rather than a field-level error.
- `reason` (string, required) — Human-readable description of why the record was rejected.

### ErrorDetail

- `status` (integer, required) — Status code as in the http standards
- `error_code` (integer, required) — Internal error code
- `title` (string, required) — Error title
- `detail` (ErrorDetailDetail, required) — Error detail
- `additional_information` (string, optional) — Optional extra information
- `traceback` (string, optional) — Optional debug information
- `interactive` (boolean, optional)

### ErrorDetailDetail

Error detail

- string
- list of string
- ErrorDetailDetail2

### ErrorDetailDetail2

All fields with validation errors

- `any_field` (list of string, optional) — Name of the field with a validation error and as a value an array with the error descriptions

## Examples

**Request**

```json
"{\"question\": \"What is the capital of France?\", \"answer\": \"Paris\"}\n{\"question\": \"What is the capital of Japan?\", \"answer\": \"Tokyo\"}"
```

**Response**

```json
{
  "accepted": 1,
  "rejected": 1,
  "errors": [
    {
      "record_index": 1,
      "field": "string",
      "reason": "string"
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints"

payload = "\"{\\\"question\\\": \\\"What is the capital of France?\\\", \\\"answer\\\": \\\"Paris\\\"}\\n{\\\"question\\\": \\\"What is the capital of Japan?\\\", \\\"answer\\\": \\\"Tokyo\\\"}\""
headers = {
    "Authorization": "Token <token>",
    "Content-Type": "application/x-ndjson"
}

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

print(response.json())
```

```javascript
const url = 'https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints';
const options = {
  method: 'POST',
  headers: {Authorization: 'Token <token>', 'Content-Type': 'application/x-ndjson'},
  body: '"{\"question\": \"What is the capital of France?\", \"answer\": \"Paris\"}\n{\"question\": \"What is the capital of Japan?\", \"answer\": \"Tokyo\"}"'
};

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

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints"

	payload := strings.NewReader("\"{\\\"question\\\": \\\"What is the capital of France?\\\", \\\"answer\\\": \\\"Paris\\\"}\\n{\\\"question\\\": \\\"What is the capital of Japan?\\\", \\\"answer\\\": \\\"Tokyo\\\"}\"")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Token <token>")
	req.Header.Add("Content-Type", "application/x-ndjson")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Token <token>'
request["Content-Type"] = 'application/x-ndjson'
request.body = "\"{\\\"question\\\": \\\"What is the capital of France?\\\", \\\"answer\\\": \\\"Paris\\\"}\\n{\\\"question\\\": \\\"What is the capital of Japan?\\\", \\\"answer\\\": \\\"Tokyo\\\"}\""

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints")
  .header("Authorization", "Token <token>")
  .header("Content-Type", "application/x-ndjson")
  .body("\"{\\\"question\\\": \\\"What is the capital of France?\\\", \\\"answer\\\": \\\"Paris\\\"}\\n{\\\"question\\\": \\\"What is the capital of Japan?\\\", \\\"answer\\\": \\\"Tokyo\\\"}\"")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints', [
  'body' => '"{\\"question\\": \\"What is the capital of France?\\", \\"answer\\": \\"Paris\\"}\\n{\\"question\\": \\"What is the capital of Japan?\\", \\"answer\\": \\"Tokyo\\"}"',
  'headers' => [
    'Authorization' => 'Token <token>',
    'Content-Type' => 'application/x-ndjson',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Token <token>");
request.AddHeader("Content-Type", "application/x-ndjson");
request.AddParameter("application/x-ndjson", "\"{\\\"question\\\": \\\"What is the capital of France?\\\", \\\"answer\\\": \\\"Paris\\\"}\\n{\\\"question\\\": \\\"What is the capital of Japan?\\\", \\\"answer\\\": \\\"Tokyo\\\"}\"", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Token <token>",
  "Content-Type": "application/x-ndjson"
]

let postData = NSData(data: ""{\"question\": \"What is the capital of France?\", \"answer\": \"Paris\"}\n{\"question\": \"What is the capital of Japan?\", \"answer\": \"Tokyo\"}"".data(using: String.Encoding.utf8)!)

let request = NSMutableURLRequest(url: NSURL(string: "https://api.prolific.com/api/v1/data-collection/datasets/dataset_id/datapoints")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```