Disposition Data Guide

Integrate the Disposition Data API into your ATS (Applicant Tracking System).

By using this API, this API documentation, and/or building the integration, you agree to the Additional API Terms and Guidelines.

What is disposition data?

Disposition data is information about any kind of update to an application in your applicant tracking system (ATS).

An update can be a change in a recruiter's workflow or an action that a recruiter or candidate takes after the candidate applies.

Examples of disposition data:

  • A new candidate enters the ATS
  • A candidate is:
    • Contacted
    • Interviewed
    • Given an offer
    • Rejected
    • Hired

How do I upload disposition data to Indeed?

  • Set up Indeed Apply so your ATS can receive applications from Indeed.

  • Request a Disposition Data Integration by contacting the Indeed Alliances team at marketplacesupport@indeed.com. Make sure to read the integration process overview below, the API requirements, and the API instructions to see if you can comply. If you can't comply, make sure to mention potential problem areas in your email.

Integration process overview

  1. Contact your Indeed Alliances manager (or send an email to marketplacesupport@indeed.com) to request an API key.
  2. You will receive an email with your API keys from Indeed.
  3. Store these keys in a safe place and use only for the Disposition Data Integration. Do not use them for any other Indeed integration/API.
  4. Read all of our documentation below.
  5. Send an upload using your test API key. Your test upload should contain real data, including real Indeed Apply IDs.
  6. Contact your Indeed Alliances manager and let them know the test file is ready for review.
  7. Indeed will manually review the file format and contact you within 1 week (usually sooner).
  8. Your Indeed Alliances manager will contact you with your review results. If your test file passes, you may start sending uploads to production by using your production API key.
  9. After you start sending production files, Indeed encourages, but does not require further test uploads.

API requirements

  • Upload format: .csv (preferred), .xml (for legacy integrations)
  • Upload size: Up to 1GB of data per upload. For larger files, please break them up into multiple files.
  • Upload frequency: Up to once per hour
  • Columns
    • apply_id - Indeed ApplyId. String, exactly 64 characters long.
    • disposition_timestamp - ISO-8601 format date and time when a disposition activity occurred. Timezone information is required.
    • status - Normalized disposition status, e.g. "HIRED". See the "Field Definitions" section below.

Full code examples for using the API are below.

Typical integration setup

Partners should set up a scheduled job that runs periodically to generate a report of applications with status changes. An example would be a csv with headers: disposition_timestamp, apply_id and status.

  • Each row/record would then represent an application's change in application state.
  • A single apply id can have multiple events/records per report.
  • Do NOT include data from previous uploads.
  • Do NOT include repeats of the same (apply id, status) if there was no change in status between them. Example: NEW -> REJECTED is valid, NEW -> NEW -> REJECTED is not.

Full example: An application may be updated multiple times per day and you can upload it every 24 hours. However, the same row should not appear twice or be resubmitted.

Note that the csv file should only contain applications that have had a change in status. Here are examples of correct and incorrect csv file uploads for a given series of application status changes.

Sample Correct CSV

Sample Correct XML (for legacy integrations only)



Day 1 - File 1
disposition_timestamp apply_id status
2019-01-01T01:00:00z appid001 NEW
Day 1 - File 1
disposition_timestamp apply_id status
2019-01-01T01:00:00z appid001 NEW
Day 2 - File 2
disposition_timestamp apply_id status
2019-01-02T01:00:00z appid002 NEW
Day 2 - File 2
disposition_timestamp apply_id status
2019-01-01T01:00:00z appid001 NEW
2019-01-02T01:00:00z appid002 NEW
Day 3 - File 3
disposition_timestamp apply_id status
2019-01-03T01:00:00z appid001 CONTACTED
2019-01-03T01:00:00z appid003 NEW
Day 3 - File 3
disposition_timestamp apply_id status
2019-01-02T01:00:00z appid002 NEW
2019-01-03T01:00:00z appid001 CONTACTED
2019-01-03T01:00:00z appid003 NEW

This example is correct because data is included only for

  • new applications
  • when the status of an apply_id has changed

This example is incorrect for 2 reasons:

  • (2019-01-01T01:00:00z, appid001, NEW)
    is uploaded twice where it should have only appeared on day 1
  • (2019-01-02T01:00:00z, appid002, NEW)
    is uploaded twice where it should have only appeared on day 2

Field definitions

Field Required? Description
apply_id Yes

The unique identifier for the job application that is used to reference the employer, candidate, job, and more.

This is the id field in the Indeed Apply JSON application data.

See how the id field is referenced in this JSON application file example>.

status Yes

IMPORTANT: Read this carefully.

Normalized application status. Indeed uses the Status field to understand what happens to applications once they are in your ATS. There are 6 different statuses we look for (see below). You must map all of your ATS's statuses into these 6 Indeed Statuses. It is absolutely crucial you accurately map the statuses from your ATS into the 6 categories. Without an accurate mapping, Indeed cannot reliably determine what happens to an application. If you support custom statuses in your ATS, please mention this to your Indeed Alliances manager.

Indeed Status Description
NEW The application arrived via Indeed Apply

The candidate was contacted by phone, email, etc. This status should be sent even if the application status on your ATS has not changed. Indeed uses this as a signal to help determine employer responsiveness, which can affect the quality and volume of candidates an employer receives.

INTERVIEWED The candidate has been interviewed.
OFFERED An offer of employment has been sent to a candidate.
HIRED The candidate accepted an offer of employment.
REJECTED A candidate has been explicitly rejected or all remaining candidates rejected after the role closed.

If you are unable to comply with this step, please contact your Indeed Alliances manager.

disposition_timestamp Yes

ISO 8601 format timestamp with timezone information for when the status change occurred.

E.x. November 5, 1994 at 8:15 am Eastern Time would be: 1994-11-05T08:15:30-05:00

  • The separator between date and time must be a capital 'T'.
  • Timezone information is required.
  • Use the format +/-HH:MM for timezone information, unless it is UTC (see next point).
  • For UTC, "+00:00", "Z", and "z" all valid, but "-00:00" is not valid.
  • Please use 00:00:00 for hour/minute/second if you do not have actual time information.

API instructions for exporting data to Indeed


Our API allows your organization to programmatically deposit disposition information. The integration takes two steps:

  1. Obtain a presigned Amazon S3 URL by POST request to this API using the assigned API Key.

  2. Make a PUT request to upload the file to the URL returned in Step 1.


To prevent uploading files with the same name, use a naming convention that allows you to distinguish your files from other files in your organization.

Obtaining a signed S3 URL

To receive the signed S3 bucket URL, create a POST request to our API with the file names you are uploading. A presigned upload URL will be returned for each file. Requirements and responses are described below.

Required request headers

  • Content-Type: application/json
  • Accept: application/json
  • token: <YOUR API KEY>
    • Type: string, 32 characters


  • file_names
    • Type: JSON list of file names to get an upload URL.
    • Note: Please do not pass a file path here, only the file's name
  • Accepted Extensions: csv, xml


  • Files must not be larger than 1 GB.
  • The URL generated will expire in 1 hour.
  • All files must pass extension validation. Files with unsupported extensions will not upload.

Success response

A HTTP status of 200 and a JSON object/dictionary will be returned with keys as the file_names sent and values corresponding to the presigned S3 URL. See examples below for details.

Error response

A HTTP status non 200 and dictionary containing the key Error will be returned and give more information.

Uploading files

Once you have the presigned URL, upload files to the AWS Bucket using a PUT request. An example is shown below, however you may also refer to the official AWS documentation.

Code examples


#  Get the presigned URLs
curl \
-H 'Content-Type: application/json' \
-H 'token: XXXXXXXXX' \
-H 'Accept: application/json' \
--request POST \
--data '{"file_names":["test_file_1.csv","test_file_2.csv"]}' \
##### API Response #####
# {
#    "test_file_1.csv": "https://blah.s3.amazonaws.com/presigned_url_1",
#    "test_file_2.csv": "https://blah.s3.amazonaws.com/presigned_url_2"
# }
##### END Response #####
#  Upload files to S3, one at a time.
#  Put pre-signed URL in quotes.
curl -X PUT -T /path/to/test_file_1.csv \
-L https://blah.s3.amazonaws.com/presigned_url_1
#  Put pre-signed URL in quotes.
curl -X PUT -T /path/to/test_file_2.csv \
-L https://blah.s3.amazonaws.com/presigned_url_2


import requests
import json

url = 'https://indeed-atsdi.com/api/get_upload_url' file_paths = ['/path/to/test_file_1.csv', '/path/to/test_file_2.csv'] API_KEY = 'XXXXXXX'

# File tuple [0] = path, [1] = basename. file_infos = [(fp, ntpath.basename(fp)) for fp in file_paths]

headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'token': API_KEY }

# This gets the presigned URLs r = requests.post(url, headers=headers, data=json.dumps({'file_names': [fi[1] for fi in file_infos]})) try: r.raise_for_status() except: print("Error occurred.") print (r.json())

# Upload file(s) to AWS. s3_signed_urls = r.json() for file_info in file_infos: with open(file_info[0], 'rb') as data: upload_result = requests.put(s3_signed_urls[file_info[1]], data=data) try: upload_result.raise_for_status() except: print("Issue uploading file to AWS.")


require 'net/http'
require 'net/https'
require 'uri'
require 'json'

Tuple = Struct.new(:_1, :_2)

api_url = URI.parse('https://indeed-atsdi.com/api/get_upload_url') file_paths = ['/path/to/test_file_1.csv', '/path/to/test_file_2.csv'] api_key = 'XXXXXXX'

names = [] file_info = [] file_paths.each do |path| file_info.push(Tuple.new(path, File.basename(path))) names.push(File.basename(path)) end

header = {'Accept': 'application/json', 'Content-Type': 'application/json', 'token': api_key} data = {file_names: names}

# Access indeed api api_response = nil Net::HTTP.start(api_url.host, :use_ssl => true) do |http| api_response = http.send_request("POST", api_url.request_uri, data.to_json, header) end

case api_response when Net::HTTPSuccess json = JSON.parse(api_response.body)

# Upload to S3. file_info.each do |upload_file| s3_url = URI.parse(json[upload_file._2]) file = File.open(upload_file._1, "rb") file_data = file.read file.close s3_response = nil Net::HTTP.start(s3_url.host, :use_ssl => true) do |http| s3_response = http.send_request('PUT', s3_url.request_uri, file_data, { # Content type has to be here, even if set to '', or else 403 error. "content-type" => '', }) end case s3_response when Net::HTTPSuccess print 'Successfully uploaded ' + upload_file._1 + "\n" else print 'Error uploading file ' + upload_file._1 + "\n" print s3_response.inspect + "\n" end end else print 'Bad Response from Indeed API.' + "\n" print api_response.inspect + "\n" end


<? php
class IndeeddispositionClient{
	private static $api_key = 'XXXXXXXXX';
	private static $api_url = 'https://indeed-atsdi.com/api/get_upload_url';

static function get_s3_url($filename) { $url = self::$api_url; $data = '{"file_names":["'.$filename.'"]}'; $headers = array( 'Content-Type: application/json', 'token: ' . self::$api_key, 'Accept: application/json' ); $options = array( CURLOPT_POST => 1, CURLOPT_POSTFIELDS => $data, CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => 1 ); $curl = curl_init($url); curl_setopt_array($curl, $options); $res = curl_exec($curl); curl_close($curl); return $res; } static function upload_to_s3($file_path_string, $signed_url){ $fh = fopen($file_path_string, 'rb'); $options = array( CURLOPT_VERBOSE => 1, CURLOPT_POST => 1, CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $signed_url, CURLOPT_INFILE => $fh, CURLOPT_INFILESIZE => filesize($file_path_string), CURLOPT_PUT => 1 ); $ch = curl_init(); curl_setopt_array($ch, $options); $result = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } fclose($fh); curl_close($ch); return $result; } }

$file_paths = array("test_file_1.csv","test_file_2.csv"); foreach ($file_paths as $file_path){ if (!file_exists($file_path)) { print 'Cannot find file '.$file_path."\n"; throw new Exception('File not found '.$file_path); } $file_name = basename($file_path); print "Uploading ".$file_name ."\n"; print "... ".$file_name ."\n"; $s3_url = json_decode(IndeeddispositionClient::get_s3_url($file_name), true)[$file_name]; $result = IndeeddispositionClient::upload_to_s3($file_path, $s3_url);

print $result;

print "\nFinished with ".$file_name ."\n"; } s?>

Frequently asked questions

No. Please do not submit empty files.

No. Please aggregate your data before sending it. That said, we are currently working on a solution to support more frequent upload intervals. If this functionality interests you, please let your Indeed Alliances manager know when you get in touch with them.

Please reach out to an Indeed Alliances manager for assistance.

This is a problem with S3, with a tragically opaque error name. Double-check the headers you are sending. Do NOT send a "Content-Type" header to S3. Refer to S3 documentation if you continue to have this issue.

Request help

To receive assistance, please contact our integration support team at: marketplacesupport@indeed.com


While our integration documentation is publicly available as a reference to understand and plan your integration with Indeed, it is intended to be implemented only by ATS partners who have signed a Master Services Agreement with us.

Terms of Service