Integrating with the API

The Appen API allows you to post data to, launch, and receive results from Appen jobs in an automated fashion. This document provides an overview of the necessary process to complete a typical Appen API integration. For best practices when integrating please review this article. The following topics are covered in this article:


Please visit this link for our new Developer API Documentation.


Appen uses a RESTful API that accepts data as URL-encoded key value pairs. Responses are restricted to JSON format and authentication is key-based.

Prior to integrating with the Appen API, you will need to generate your API key.

1. First, click the "Account" icon located in the bottom corner of the Global Navigation bar on the left side of any page.

2. Click the "API" tab and find your key listed under the "Your API Key" section.


3. Click on Generate New. After the API Key displays in plain text, you will be able to click on Copy Key. Store the API Key securely outside of ADAP. Once you leave the API page, the API Key will be masked and you will not be able to retrieve it. If have not stored the API Key, you will need to re-generate another API Key.



Posting Data

With Appen’s API you can post data unit by unit (row by row) or via CSV or JSON upload. Posting unit by unit is preferred because it allows you to send data in real time and facilitates more granular monitoring and error handling capabilities.

Below we provide you with two different implementations for posting data to the Appen API: unit by unit and with a CSV file. Please observe the following conventions:

URLs: All URLs begin with the following pattern -

Authentication: Each request to the API must contain a key parameter with your API key (see examples below).

Format: The API currently only supports JSON. You must set the HTTP header “Accept:” application/json or append .json to the URL of the request (see examples below).

Unit by Unit Posting Examples


curl -d 'unit[data][column1]=helloworld' -d 'unit[data][column2]=helloworld2' '{job_id}/units.json?key={Your_Appen_API_Key}'

Ruby HTTParty Gem:

require 'httparty'   

  API_KEY = "Your_Appen_API_Key"
  job_id = "Your_Job_ID_or_Alias"
  data = {column1:"helloworld", column2:"helloworld2"}
  "{job_id}/units.json", :query => {
  :key => API_KEY,
  :unit => {
  :data => data,
  :state => :new
}, :timeout => 5

Ruby Appen Gem:

require 'figure-eight'

  API_KEY = "Your_Appen_API_Key"
  job_id = "Your_Job_ID_or_Alias"

  Appen::Job.connect! API_KEY, DOMAIN_BASE
  job =
  unit =
  unit.create({column1:"helloworld", column2:"helloworld2"})

Python Requests Lib:

import requests
    import json

    API_KEY = "Your_Appen_API_Key"
    job_id = "Your_Job_ID_or_Alias"

    data = {'column1': 'helloworld', 'column2': 'helloworld2'}

    request_url = "{}/units.json".format(job_id)
    headers = {'content-type': 'application/json'}

    payload = {
    'key': API_KEY,
    'unit': {
    'data': data
}, data=json.dumps(payload), headers=headers)


CSV-Based Posting Examples


curl -X PUT -T 'sampledata.csv' -H 'Content-Type: text/csv'{job_id}/upload.json?key={Your_Appen_API_Key}

Note: the fie uploaded must be UTF-8 encoded.

Ruby HTTParty Gem:

require 'httparty'   

  API_KEY = "Your_Appen_API_Key"
  job_id = "Your_Job_ID_or_Alias"

  :body =>"sample.csv"),
  :headers => {"content-type" => "text/csv"},
  :query => { :key => API_KEY}

Ruby Appen Gem:

require 'figure-eight'

  API_KEY = "Your_Appen_API_Key"
  job_id = "Your_Job_ID_or_Alias"

  Appen::Job.connect! API_KEY, DOMAIN_BASE
  job =
  job.upload(File.dirname(__FILE__) + "/sample.csv", "text/csv")

Python Requests Lib:

import requests
  import json

  API_KEY = "Your_Appen_API_Key"
  job_id = "Your_Job_ID_or_Alias"

  file_path = "sample.csv"
  csv_file = open(file_path, 'rb')

  request_url = "{}/upload".format(job_id)
  headers = {'content-type': 'text/csv'}
  payload = { 'key': API_KEY }

  requests.put(request_url, data=csv_file, params=payload, headers=headers)



After your request, an HTTP status code will be sent in response, indicating whether your request was successful (See Below for a list of status codes and their definitions). If the status code of a response is something other than 200, you will find a JSON message in the body of the response that will give you the status of a given operation or report an error. Appen returns one of three messages: :


{message: {success: "Job created successfully."}}
  {message: {notice: "Your job is being completed."}}
  {message: {error: "Job could not be canceled."}}


API Security

Appen uses authentication keys to ensure that APIs are only accessible to those with the proper privileges. All API postings are made over a Secure Sockets Layer (SSL) connection, which encrypts communications between the user and web server to ensure data remains private. Note that this means all requests must be prepended by https://.


PHP API Integration

To integrate with the Appen API using PHP, please visit

Note: is an open source package. Appen is not responsible for the maintenance and functionality of this package. Please contact the GitHub owner if you have any questions about the package.


Receiving Results via Webhook URI

Warning: For currently enabled webhooks that uses API keys as part of webhook signature. (Webhooks created before June 27, 2022)

Real-time delivery of Appen results is handled via a webhook that provides callbacks between Appen and your web application.

Enter the webook in the Job Settings - API tab

Appen will fire HTTP POST requests to your Webhook URI as each unit in a job reaches its finalized state.  Each POST will feature three parameters: signal, payload and signature.

  • The signal defines the payload. Results for a unit will be delivered with a "unit complete" signal.
  • The payload parameter contains a JSON representation of all the data associated with the unit (See below for a verbose explanation of the payload parameter).
  • The signature contains a SHA1 encrypted version of your secret key concatenated with the payload i.e., signature = sha1_encrypt(payload + api_key).


Your webhook server will need to provide a 200 response with valid JSON regardless of whether your application accepts the request.

Webhook Security

Using the webhook's signature: You will be able to sign Appen's posts by validating the sha1 signature attribute in the body of the payload.  The signature is a sha1 encrypted hash of the unparsed payload concatenated with your secret API key (i.e., signature = sha1(payload + your_api_key)). You can validate the webhook by generating the same sha1 hash and comparing it to the signature sent with the payload (e.g., webhook.valid? if payload[:signature] == sha1(payload + my_api_key))

Via basic authentication: If your endpoint (webhook) supports basic authentication, you can set the webhook in your job using the following convention: 'http://username:password@webhook'. The webhook will be posted to your server using HTTP basic authentication via the username:password credentials that you provide.


The Payload Attribute of a Webhook Post

At the top level in the payload you will encounter the following attributes:

  • results – Any unit that has gathered trusted judgments will also have a results attribute.   The results attribute is a hash containing all of the results and judgment data for the unit.
  • job_id – This is the numerical id of the job on Appent’s platform.
  • updated_at – Time stamp for when the unit was last updated
  • data – The data attribute is a hash containing the source data for the unit.
  • judgment_count – The total count of judgments on the unit.
  • created_at – This is the time when the unit was created.
  • state – Golden (a test question), judgeable (an unfinalized unit), finalized (a finalized unit).
  • id – Appen’s unique numeric id of the unit.


It’s important to note that the results attribute may feature different fields from unit to unit depending on the following:

  1. The number of judgments that were collected for the unit
  2. The content of each of the judgments (what questions in the form were responded to).

Each of the child attributes of the results attribute are discussed in greater detail below.

Field Results

Within the results hash, there is a field result attribute for each question in the job that received a judgment. This attribute represents the final answer of that question, and is itself presented as a hash. These attributes will be keyed according to how they are named in the job.




Here, confidence represents the confidence value for the response that Appen deems to be correct. Agg represents the value of the correct answer itself.

Note: Often, the form in a job is designed with logical contingencies that determine the questions that are asked for any given unit. Field results only exist for questions/fields that received judgments. So if every Appen contributor answered the same for a given unit without exposing a logic-hidden field, that field will not exist in the results hash.


Results also contains a top-level attribute judgments, which contains a JSON array of all the Judgments that have been gathered so far during your job. This section of the unit.json is analogous to a row in a Appen full CSV, which contains every value for every judgment submitted for a job. Note the definitions for each key in bold.

    "city":"San Francisco",


Let’s see what it looks like with definitions instead:

    "worker_id": Appen Contributor ID,
    "city": worker city
    "job_id": job number (ID),
    "external_type": Channel via which the contributor entered the job.
    "tainted": Boolean – is judgment from an untrusted contributor,
    "data": {
      “data” is a hash attribute that contains a workers responses to each of the questions they responded to. It is the judgment level version of the field result attribute:
      “unit_data is a hash attribute that contains the initial data that you posted for this unit (also called "source data"):
    "trust": The trust of the contributor,
    "golden": Boolean - is this a gold (test) unit or not,
    "judgment": Numeric indices of the judgment for this unit,
    "created_at": Timestamp for the submission of the judgment,
    "unit_id": Appen’s numeric ID for the unit,
    "unit_state": State of the unit, should always read: "finalized",
    "region": Worker region,
    "country": Worker country code,
    "rejected": If the contributor was rejected or not,
    "started_at":Timestamp for the start of the judgment,
    "id": Judgement ID (Unique identifier),
    "worker_trust": Contributor trust (accuracy on hidden test questions),
    "missed": You may safely ignore this attribute.

Status Codes

The potential HTTP status codes that may be returned following an API request are listed in the Responses & Messaging article


Rate Limiting

Rate limit enforced in Appen's API: 40 requests every second

Was this article helpful?
4 out of 7 found this helpful

Have more questions? Submit a request
Powered by Zendesk