Disposition Data CSV guide
Integrate Disposition Data CSV into your applicant tracking system (ATS).
Note:
By using this API and its documentation and building an integration, you agree to the Additional API Terms and Guidelines.
Disposition data integration process
Disposition data is information about an update to an app in your applicant tracking system (ATS), such as change in a recruiter's workflow or a recruiter's or candidate's action that occurs after the candidate applies.
Note:
Examples of disposition data are:
- A candidate enters the ATS
- A candidate is contacted, interviewed, given an offer, rejected, or hired
To upload disposition data to Indeed:
1. |
To enable your ATS to receive applications from Indeed, set up Indeed Apply. |
2. |
Request test and production API keys. Either contact your Indeed Alliances manager or email [email protected]. Indeed emails you the API keys.
|
3. |
Review API requirements. |
4. |
|
5. |
Contact the Indeed Alliances team at [email protected] to request a Disposition Data integration. If you cannot comply with the previous requirements, list the issues in your email. |
API requirements
Overview
Requirement | Description |
---|---|
Upload format |
|
Upload size |
Up to 1 GB of data per upload. Split larger files into multiple files. |
Upload frequency |
Up to once per hour. |
|
Typical integration setup
Partners set up a scheduled job that runs periodically to generate a report of applications with status changes. For example, generate a CSV file with these headers: disposition_timestamp
, apply_id
, and status
.
The CSV file requirements are:
-
Each row, or record, represents a change in app state.
-
A single apply ID can have multiple events, or records, per report.
-
Do NOT include data from earlier uploads.
-
Do NOT include repeats of the same fields, such as apply ID or status, if no change in status occurred.
Example:
NEW -> REJECTED
is valid, whileNEW -> NEW -> REJECTED
is not.
Full example: You can update an app multiple times a day, and you can upload it every 24 hours. However, you cannot resubmit the same row and it cannot appear twice.
Note:
The CSV file can only contain applications with a change in status. Download the following correct CSV file and XML file for a series of app status changes.
- Download: Example CSV file
- Download: Example XML file (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 it includes data:
|
This example is incorrect for these reasons:
|
Field definitions
All fields are required.
Field | Description | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
apply_id
|
The unique ID for the job app that references the employer, candidate, job, and more. This is the |
||||||||||||||
status
|
If you cannot comply with this requirement, contact your Indeed Alliances manager. |
||||||||||||||
disposition_timestamp
|
ISO 8601-formatted timestamp with time zone information for when the status change occurred. For example, format November 5, 1994 at 8:15 am Eastern Time as:
|
Export data to Indeed
This API enables your organization to programmatically submit disposition information:
1. |
To get a signed Amazon S3 URL, make a |
2. |
To upload the file, make a |
Note:
To prevent uploading files with the same name, use a naming convention that enables you to distinguish your files from other files in your organization.
Get a signed S3 URL
To get the signed S3 bucket URL, create a POST
request to the API with the file names you are uploading. A presigned upload URL is returned for each file.
The requirements for this API call are:
Requirement | Description |
---|---|
Request headers |
|
Parameters |
|
Limitations |
|
If the request succeeds, the API returns the HTTP 200
status code and a JSON dictionary with the list of file_names
and their corresponding presigned S3 URL.
If the request fails, the API returns an HTTP non-200
status code and a JSON dictionary with the Error
key, which provides information about the error.
Upload files
After you have the presigned URLs, make a PUT
request to upload files to the AWS Bucket.
View the following examples.
Code examples
curl
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
The response is:
{
"test_file_1.csv": "https://blah.s3.amazonaws.com/presigned_url_1",
"test_file_2.csv": "https://blah.s3.amazonaws.com/presigned_url_2"
}
Upload files one at a time to S3. 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
Python
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.")
Ruby
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
<? 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
Should I send an empty file for periods that have no data?
No. Do not submit empty files.
Can I send data in smaller intervals than one hour?
No. Aggregate your data before sending it. That said, Indeed is currently working on a solution to support more frequent upload intervals. If this functionality interests you, let your Indeed Alliances manager know.
My ATS supports custom statuses. How can I map them appropriately?
Reach out to an Indeed Alliances manager for assistance.
Why do I get a SignatureDoesNotMatch
error during a file upload?
Double-check the headers you are sending. Do NOT send a Content-Type
header to S3.
If you continue to have this issue, see the Troubleshoot signed requests for AWS APIs.
Request help
For help, contact our integration support team at [email protected].
Disclaimer
While our integration reference is publicly available, it is intended to be implemented only by ATS partners who have signed a Master Services Agreement with Indeed.
See also
- For examples of using the API, see Code examples.
- Uploading objects with presigned URLs.
Updated 3 days ago