> 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/ai-task-builder/append-dataset-datapoints

## OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: Prolific API for Data Collectors
  version: 1.0.0
paths:
  /api/v1/data-collection/datasets/{dataset_id}/datapoints:
    post:
      operationId: append-dataset-datapoints
      summary: Append Datapoints to a Dataset
      description: >-
        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.
      tags:
        - aiTaskBuilder
      parameters:
        - name: dataset_id
          in: path
          description: The unique identifier of the V4 dataset to append to
          required: true
          schema:
            type: string
            format: uuid
        - name: Authorization
          in: header
          description: >-
            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>`.
          required: true
          schema:
            type: string
      responses:
        '200':
          description: >-
            Append processed. Returns per-request accepted/rejected counts. A
            `200` is returned even

            when some (or all) records were rejected — inspect `rejected` and
            `errors`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DatasetAppendResult'
        '400':
          description: >-
            Bad request — missing body, CSV payload, empty JSONL, too many
            records, a non-V4 dataset,

            or a dataset with no schema.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            Forbidden — the user does not have access to the dataset's
            workspace.
          content:
            application/json:
              schema:
                description: Any type
        '404':
          description: Dataset not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            Conflict — a dataset import or a schema migration is in progress.
            Retry once it completes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      requestBody:
        content:
          application/json:
            schema:
              type: string
servers:
  - url: https://api.prolific.com
    description: Production
components:
  schemas:
    DatasetAppendError:
      type: object
      properties:
        record_index:
          type: integer
          description: >-
            One-based index of the rejected non-blank JSONL record in the append
            request body.
        field:
          type: string
          description: >-
            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:
          type: string
          description: Human-readable description of why the record was rejected.
      required:
        - record_index
        - field
        - reason
      description: A record-level validation error from a synchronous JSONL append request.
      title: DatasetAppendError
    DatasetAppendResult:
      type: object
      properties:
        accepted:
          type: integer
          description: >-
            Number of records that passed validation (newly written or
            idempotent duplicates).
        rejected:
          type: integer
          description: Number of records rejected. Equal to the length of `errors`.
        errors:
          type: array
          items:
            $ref: '#/components/schemas/DatasetAppendError'
          description: Record-level rejections. Empty when every record was accepted.
      required:
        - accepted
        - rejected
        - errors
      description: >-
        Result of a synchronous append to a V4 dataset (`POST
        /datasets/{dataset_id}/datapoints`).

        `accepted` counts records that passed validation (newly written plus
        idempotent duplicates);

        `rejected` counts records that failed parsing or schema validation, each
        detailed in `errors`.
      title: DatasetAppendResult
    ErrorDetailDetail2:
      type: object
      properties:
        any_field:
          type: array
          items:
            type: string
          description: >-
            Name of the field with a validation error and as a value an array
            with the error descriptions
      description: All fields with validation errors
      title: ErrorDetailDetail2
    ErrorDetailDetail:
      oneOf:
        - type: string
        - type: array
          items:
            type: string
        - $ref: '#/components/schemas/ErrorDetailDetail2'
      description: Error detail
      title: ErrorDetailDetail
    ErrorDetail:
      type: object
      properties:
        status:
          type: integer
          description: Status code as in the http standards
        error_code:
          type: integer
          description: Internal error code
        title:
          type: string
          description: Error title
        detail:
          $ref: '#/components/schemas/ErrorDetailDetail'
          description: Error detail
        additional_information:
          type: string
          description: Optional extra information
        traceback:
          type: string
          description: Optional debug information
        interactive:
          type: boolean
      required:
        - status
        - error_code
        - title
        - detail
      title: ErrorDetail
    Error:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
      required:
        - error
      title: Error
  securitySchemes:
    token:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        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>`.

```

## Examples



**Request**

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

**Response**

```json
{
  "accepted": 1,
  "rejected": 1,
  "errors": [
    {
      "record_index": 2,
      "field": "answer",
      "reason": "Expected a string but received a number"
    }
  ]
}
```

**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\\\": 12345}\""
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\": 12345}"'
};

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\\\": 12345}\"")

	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\\\": 12345}\""

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\\\": 12345}\"")
  .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\\": 12345}"',
  '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\\\": 12345}\"", 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\": 12345}"".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()
```