Disposition Data Guide
What is disposition data?
Disposition data is any kind of update that happens to an application in your Applicant Tracking System (ATS). Updates can include changes in a recruiter's workflow or actions a recruiter/candidate takes after they apply.
- A new candidate entered the ATS
- A candidate was:
- contacted
- interviewed
- given an offer
- rejected
- hired
Why send disposition data to Indeed?
-
When you send Indeed disposition data about job applications, we can make better decisions about the amount and type of job applications we send you in the future.
-
It helps job seekers. The Disposition Data Integration helps job seekers by giving Indeed a better idea of which jobs are still active and which employers are responsive. You can "close the loop" with job applications that start on Indeed.
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
- Contact your Indeed Alliances manager (or send an email to marketplacesupport@indeed.com) to request an API key.
- You will receive an email with your API keys from Indeed.
- 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.
- Read all of our documentation below.
- Send an upload using your test API key. Your test upload should contain real data, including real Indeed Apply IDs.
- Contact your Indeed Alliances manager and let them know the test file is ready for review.
- Indeed will manually review the file format and contact you within 1 week (usually sooner).
- 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.
- 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 XML (for legacy integrations only)
Correct |
Incorrect |
|||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Day 1 - File 1
| Day 1 - File 1
|
|||||||||||||||||||||
Day 2 - File 2
|
Day 2 - File 2
|
|||||||||||||||||||||
Day 3 - File 3
|
Day 3 - File 3
|
|||||||||||||||||||||
This example is correct because data is included only for
|
This example is incorrect for 2 reasons:
|
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.
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:
|
API instructions for exporting data to Indeed
Overview/Integration
Our API allows your organization to programmatically deposit disposition information. The integration takes two steps:
-
Obtain a presigned Amazon S3 URL by
POST
request to this API using the assigned API Key. -
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
Parameters
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
Limitations
- 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
Use a unix-based system with bash and the curl
command.
# 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"]}' \
https://indeed-atsdi.com/api/get_upload_url
##### 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
Use Python 3 and the requests
library.
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.")
Use Ruby and only standard packages.
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
Use PHP with standard libraries and curl PHP module.
<? 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";
}
?>
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
Disclaimer
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.