Full Apply
This page specifies how to formulate a request to apply to a job which requires a more thorough application request. The JSON response from the /jobs/search endpoint for such jobs will meet the follow two criteria:
- The
apply_methodfield for the job will equal"full_apply_endpoint" - The
number_of_questions_allowedfield will have a value than 0
The email, full name, questions and the Partner Attributes contained in the Job Search response are required. As with almost all other requests to the Apploi API, the X-Api-Key header is required in the request and should contain the API key given to the partner to be able to use the API.
Questions For Apploi Jobs.
A JSON representation of the questions related to a job can be retrieved using the questions_url field that is contained in the response from the /jobs/search endpoint. The questions_url should look like this:
'https://ats-integrations.apploi.com/v1/apploi/{{job_id}}/questions.json?source=None'
In fact, that template should be usable in place of a call to the /jobs/search endpoint, by replacing {{job_id}} with the ID of the specific job for which question data needs to be retrieved. If using that template doesn't work, then an explicit call to the /jobs/search endpoint can/should be made.
Example Questions Data
Here is an example of the some of the JSON body returned from a call to the questions_url URL mentioned above. The example contains an entry for a few of the most common types of available questions: textarea, date, select, and multiselect.
{
"questions_en": [
{
'id': 1893206,
'question': 'Do you currently hold an active license as a professional counselor for the state of Texas?',
'required': True,
'type': 'textarea'
},
{
'id': 1920662,
'question': 'Date of Birth',
'required': True,
'type': 'date',
'format': 'dd/MM/yyyy'
},
{
'id': 1854409,
'question': 'Do you require Visa sponsorship? ',
'required': True,
'type': 'select',
'options': [{'value': 0, 'label': 'Yes'}, {'value': 1, 'label': 'No'}]
},
{
'id': 1841349,
'question': 'Do you have MHE Experience? (Forklift, Stockpicker, Reach Truck)',
'required': True,
'type': 'multiselect',
'options': [
{'value': 0, 'label': 'No experience; Need Training'},
{'value': 1, 'label': 'Experienced/Certified'},
{'value': 2, 'label': 'Some Experience; Require Additional Training'}
]
}
],
"questions_es": ["This will contain an array with the same data, but in Spanish"]
}Question Schema
The following table provides an explanation of the specific fields returned in response to a call to the questions_url .
| Name | Schema | Description | Allowed values | Example |
|---|---|---|---|---|
| id | Yes | Unique ID for this question. | any string | "Yes" |
| type | Yes | Describes the type of question. Refer to the following sections for a full explanation of each question type. |
| |
| question | Yes | The question text that is displayed to applicants. | any string | “Veteran status?” |
| format | Yes for type=date or type=text | Use the format for date or integer answers |
| |
| max | Yes format=integer | For format=integer | any integer for format=integer | 100 |
| min | Yes format=integer | For format=integer | any integer for format=integer | 0 |
| required | Yes | Allows the setting of a question as required. When required:true is present, Indeed Apply forces a non-empty text field or a non-empty selection. When the required parameter is not present, questions are optional. If a question is optional, a possible answer value is “”. |
| |
| Options | Yes for type=select and type=multiselect | The list of options available in the drop-down menu. | JSON | This page specifies how to formulate a re |
| Label | Yes for type=select and type=multiselect | The text for options in any drop-down menu. | any string | Female |
| Value | Yes for type=select and type=multiselect | The value corresponding to each option that will be returned for questions with drop-down menus. | any string | 1 |
Property by Question Type
Not all of the possible fields will be provided for every question returned in the response. Some of the fields returned will be included based on the type of question.
| Property/Question Type | Text | Select | Multiple | Date | Slider(number) |
|---|---|---|---|---|---|
| id | ✅ | ✅ | ✅ | ✅ | ✅ |
| question | ✅ | ✅ | ✅ | ✅ | ✅ |
| required | ✅ | ✅ | ✅ | ✅ | ✅ |
| format | ❌ | ❌ | ❌ | ✅ | ✅ |
| label | ❌ | ✅ | ✅ | ❌ | ❌ |
| value | ❌ | ✅ | ✅ | ❌ | ❌ |
| min | ❌ | ❌ | ❌ | ❌ | ✅ |
| max | ❌ | ❌ | ❌ | ❌ | ✅ |
Answer Schema
The expected schema for answer objects in a request is provided below. Each answer is checked against the job's questions. An ID that isn't one of the job's questions, a select or multiselect option that doesn't exist, a missing value or values, or a date in another format returns 400, with a developer_message that explains the problem. Answers to text and slider questions aren't checked this way. An empty answer, or a slider answer that isn't a number, is not saved, and no error is returned. Required questions are not enforced, so make sure the applicant answers them.
| Parameter | Value | Required | Description |
|---|---|---|---|
| id | Numeric string | Yes | The ID of the question for which this object provides an answer. The available question ids can be obtained from the question data retrieved as described above. |
| value | String | Yes (for all answer types except multiselect) | The string provided by this key/value pair represents the answer to the question. The precise string depends on what type of answer is required. The type of answer and the possible values are defined in the example questions data above. Different examples are also provided in the sample request above. |
| values | Array of strings | Yes (for multiselect answers)No (for all other answer types) | An array of numeric strings. Each item in the array represents an answer option for a question which requires a multiselect answer. |
Answer types and associated formats
Here is another way of representing the data above, classified by answer type.
| Answer Type | JSON Format |
|---|---|
| text, slider | {"id":string, "value":string} |
| select | {"id":string, "value": "numeric_string"} |
| calendar | {"id":string, "value": string(dd/MM/yyyy)} |
| multiselect | {"id":string, "values": [string_1, string_2, string_n]} |
Example Request
URL
Note: Check the /easy-apply documentation and remember to add in the headers the x-api-key.
https://partners.apploi.com/easy-apply
Body
{
"applicant": {
"email": "{{email}}",
"fullName": "{{fullName}}",
"phoneNumber": "{{phoneNumber}}",
"resume": {}
},
"get_person_token": 1,
"enqueue": true,
"job": {
"jobId": "{{job_id}}"
},
"partner_attributes": {
"sponsored": true,
"redirect_apply": "https://jobs-univision.apploi.com/view/468036?utm_campaign=integration&utm_medium=job-board-search&utm_source=<utm_source>-boosted&ajs_event=LOAD_JOB_PAGE&ajs_aid=3f617530-4530-11e9-a2ca-7e775511d9e3&ajs_prop_search_fetch_id=efb330ceef1b44e3b07328adae1b1ccd&ajs_prop_keyword=&ajs_prop_page=1&ajs_prop_city_center=Unselected&ajs_prop_search_order=5&ajs_prop_job_id=468036&ajs_prop_doc_type=preferredjob&ajs_prop_job_location_lat=39.3798123&ajs_prop_job_location_lon=-74.5302804&ajs_prop_boosted=1&ajs_prop_utm_source=<utm_source>-boosted&ajs_prop_utm_medium=job-board-search&ajs_prop_utm_campaign=integration",
"utm_medium": "job-board-search",
"utm_campaign": "integration",
"keyword": "",
"search_fetch_id": "efb330ceef1b44e3b07328adae1b1ccd",
"page": 1,
"order": 5,
"city_center": "Unselected",
"utm_source": "<exploratory>-full-apply-boosted",
"source": "<source>-apply"
},
"questions": {
"answers": [
// "text" type answer
{
"id": "1786806",
"value": "Text 1"
},
// "text" type answer
{
"id": "1786807",
"value": "Text Optional"
},
// "text" type answer for a question that requires a numerical answer
{
"id": "1786810",
"value": "20"
},
// "text" type answer for a question that requires a numerical answer
{
"id": "1786811",
"value": "90"
},
// "calendar" type answer
{
"id": "1786812",
"value": "17/11/2020"
},
// "calendar" type answer
{
"id": "1786813",
"value": "18/11/2020"
},
// "select" type answer
{
"id": "1786824",
"value": "0"
},
// "select" type answer
{
"id": "1786825",
"value": "1"
},
// "multiselect" type answer
{
"id": "1786826",
"values": [
"0"
]
},
// "multiselect" type answer
{
"id": "1786827",
"values": [
"1",
"2"
]
}
]
}
}Request explained
| Parameter | Value | Required | Description |
|---|---|---|---|
| get_person_token | 1 | No | Flag used to request bearer_token, allowing you to make requests related to the user. Check Other useful endpoints |
| enqueue | true | No | |
| resume | JSON Object | No | To check the entire structure go to /easy-apply |
| questions | JSON Object | No | An object containing data about answers to the questions defined for applications to a given job. The data for the questions is described in the Questions For Apploi Jobs section above. The |
Examples
Applying to a Full Apply job takes three calls:
- Look the job up with /jobs/search. If its
external_urlis set, the employer takes applications on its own site, so send the applicant to itsredirect_apply_urlinstead. Otherwise, check that itsapply_methodisfull_apply_endpoint, and keep itsquestions_urlandpartner_attributes. - Get the job's questions from its
questions_url. Fetching it does not need your API key. - Send the application to
/easy-apply, with one answer per question inquestions.answers.
Each answer's form depends on the question's type, as described in Answer types and associated formats. Send every id and every value as a string. For select and multiselect questions, send the chosen option's value.
Replace your_partner_name with the partner name agreed with Apploi.
The examples read your API key from an environment variable named APPLOI_API_KEY. Before running them, set APPLOI_API_KEY in your own environment to the Partner API key you received from Apploi. The examples do not set it for you, and the key should never be written into your code.
The Python, TypeScript and Ruby examples answer each question with a placeholder: the first option, a fixed date, or sample text. Replace these with the applicant's real answers. For everything a response can mean, see What the response means on the /easy-apply reference page.
# APPLOI_API_KEY must hold the Partner API key you received from Apploi.
# These examples do not set it for you: set it yourself, in your own
# environment, before running them. For example, in your shell:
# export APPLOI_API_KEY="<the API key you received from Apploi>"
# 1. Look up the job. Send the applicant to "redirect_apply_url" instead if
# "external_url" is set. Otherwise check that "apply_method" is
# "full_apply_endpoint", and note its "questions_url" and
# "partner_attributes".
curl -G https://partners.apploi.com/jobs/search \
-H "X-Api-Key: $APPLOI_API_KEY" \
--data-urlencode "job_id=468003" \
--data-urlencode "source=your_partner_name"
# 2. Get the job's questions: use the "questions_url" from step 1. Fetching it
# does not need your API key.
curl -sS "https://ats-integrations.apploi.com/v1/apploi/468003/questions.json?source=your_partner_name"
# 3. Send the application, with one answer per question from step 2, and the
# job's partner_attributes from step 1, unchanged. The answers below are for
# the example questions shown earlier on this page.
curl -sS -X POST https://partners.apploi.com/easy-apply \
-H "X-Api-Key: $APPLOI_API_KEY" \
-H "Content-Type: application/json" \
-w "\nHTTP status: %{http_code}\n" \
-d '{
"applicant": {
"email": "[email protected]",
"fullName": "Jane Doe",
"phoneNumber": "555-010-0123"
},
"job": {
"jobId": "468003"
},
"partner_attributes": {
"sponsored": true,
"search_fetch_id": "394f44dc0aff4ee6ae469221e8934ee8",
"page": 1,
"order": 1
},
"questions": {
"answers": [
{ "id": "1893206", "value": "Yes, license LPC-80421" },
{ "id": "1920662", "value": "21/04/1990" },
{ "id": "1854409", "value": "1" },
{ "id": "1841349", "values": ["0", "2"] }
]
}
}'import os
import requests
# APPLOI_API_KEY must hold the Partner API key you received from Apploi.
# These examples do not set it for you: set it yourself, in your own
# environment, before running them. For example, in your shell:
# export APPLOI_API_KEY="<the API key you received from Apploi>"
API_KEY = os.environ["APPLOI_API_KEY"]
def submit_application(body):
response = requests.post(
"https://partners.apploi.com/easy-apply",
headers={"X-Api-Key": API_KEY},
json=body,
timeout=60,
)
if response.status_code == 200:
return "submitted", response.json()
if response.status_code == 409:
# The applicant has already applied to this job. Don't retry.
return "already_applied", response.json()
if response.status_code == 412:
# Only sent when "enqueue" is False. Don't retry.
result = response.json()
if "job_id" in (result.get("errors") or {}):
# The job is closed or doesn't exist.
return "job_closed", result
# The application already exists, for example from an earlier attempt.
return "already_applied", result
# 400: fix the request before sending it again. 5xx: retry with backoff.
raise RuntimeError(f"Request failed with status {response.status_code}: {response.text}")
def answer_for(question):
# Placeholder answers: replace them with the applicant's real answers.
question_id = str(question["id"]) # ids are sent as strings
if question["type"] == "select":
return {"id": question_id, "value": str(question["options"][0]["value"])}
if question["type"] == "multiselect":
return {"id": question_id, "values": [str(option["value"]) for option in question["options"][:1]]}
if question["type"] == "date":
return {"id": question_id, "value": "21/04/1990"} # DD/MM/YYYY
if question["type"] == "text":
# "format": "integer": a whole number from "min" to "max", as a string
return {"id": question_id, "value": str(question["min"])}
return {"id": question_id, "value": "Sample answer"} # textarea
# 1. Look up the job, and check that it takes Full Apply.
response = requests.get(
"https://partners.apploi.com/jobs/search",
headers={"X-Api-Key": API_KEY},
params={"job_id": "468003", "source": "your_partner_name"},
timeout=30,
)
response.raise_for_status()
jobs = response.json()["data"]
if not jobs:
raise RuntimeError("Job 468003 was not found")
job = jobs[0]
if job.get("external_url"):
# The employer takes applications on its own site.
raise RuntimeError(f"Send the applicant to {job['redirect_apply_url']}")
if job["apply_method"] != "full_apply_endpoint":
raise RuntimeError(f"Job 468003 uses {job['apply_method']}, not Full Apply")
# 2. Get the job's questions. Fetching questions_url does not need your API key.
response = requests.get(job["questions_url"], timeout=30)
response.raise_for_status()
questions = response.json()["questions_en"]
# 3. Send the application with the answers.
body = {
"applicant": {
"email": "[email protected]",
"fullName": "Jane Doe",
"phoneNumber": "555-010-0123",
},
"job": {"jobId": job["id"]}, # a string, not a number
"partner_attributes": job["partner_attributes"], # sent back unchanged
}
answers = [answer_for(question) for question in questions]
if answers:
body["questions"] = {"answers": answers}
outcome, result = submit_application(body)
print(outcome, result)// APPLOI_API_KEY must hold the Partner API key you received from Apploi.
// These examples do not set it for you: set it yourself, in your own
// environment, before running them. For example, in your shell:
// export APPLOI_API_KEY="<the API key you received from Apploi>"
if (!process.env.APPLOI_API_KEY) {
throw new Error("Set APPLOI_API_KEY to the Partner API key you received from Apploi");
}
const API_KEY: string = process.env.APPLOI_API_KEY;
type Outcome = "submitted" | "already_applied" | "job_closed";
async function submitApplication(body: object): Promise<{ outcome: Outcome; result: any }> {
const response = await fetch("https://partners.apploi.com/easy-apply", {
method: "POST",
headers: { "X-Api-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (response.status === 200) {
return { outcome: "submitted", result: await response.json() };
}
if (response.status === 409) {
// The applicant has already applied to this job. Don't retry.
return { outcome: "already_applied", result: await response.json() };
}
if (response.status === 412) {
// Only sent when "enqueue" is false. Don't retry.
const result = await response.json();
if (result.errors?.job_id !== undefined) {
// The job is closed or doesn't exist.
return { outcome: "job_closed", result };
}
// The application already exists, for example from an earlier attempt.
return { outcome: "already_applied", result };
}
// 400: fix the request before sending it again. 5xx: retry with backoff.
throw new Error(`Request failed with status ${response.status}: ${await response.text()}`);
}
type Answer = { id: string; value: string } | { id: string; values: string[] };
function answerFor(question: any): Answer {
// Placeholder answers: replace them with the applicant's real answers.
const id = String(question.id); // ids are sent as strings
switch (question.type) {
case "select":
return { id, value: String(question.options[0].value) };
case "multiselect":
return { id, values: question.options.slice(0, 1).map((option: any) => String(option.value)) };
case "date":
return { id, value: "21/04/1990" }; // DD/MM/YYYY
case "text":
// "format": "integer": a whole number from "min" to "max", as a string
return { id, value: String(question.min) };
default:
return { id, value: "Sample answer" }; // textarea
}
}
// 1. Look up the job, and check that it takes Full Apply.
const lookupParams = new URLSearchParams({ job_id: "468003", source: "your_partner_name" });
const lookupResponse = await fetch(`https://partners.apploi.com/jobs/search?${lookupParams}`, {
headers: { "X-Api-Key": API_KEY },
});
if (!lookupResponse.ok) {
throw new Error(`Request failed with status ${lookupResponse.status}`);
}
const job = (await lookupResponse.json()).data[0];
if (!job) {
throw new Error("Job 468003 was not found");
}
if (job.external_url) {
// The employer takes applications on its own site.
throw new Error(`Send the applicant to ${job.redirect_apply_url}`);
}
if (job.apply_method !== "full_apply_endpoint") {
throw new Error(`Job 468003 uses ${job.apply_method}, not Full Apply`);
}
// 2. Get the job's questions. Fetching questions_url does not need your API key.
const questionsResponse = await fetch(job.questions_url);
if (!questionsResponse.ok) {
throw new Error(`Request failed with status ${questionsResponse.status}`);
}
const questions: any[] = (await questionsResponse.json()).questions_en;
// 3. Send the application with the answers.
const answers = questions.map(answerFor);
const { outcome, result } = await submitApplication({
applicant: {
email: "[email protected]",
fullName: "Jane Doe",
phoneNumber: "555-010-0123",
},
job: { jobId: job.id }, // a string, not a number
partner_attributes: job.partner_attributes, // sent back unchanged
...(answers.length > 0 && { questions: { answers } }),
});
console.log(outcome, result);require "json"
require "net/http"
require "uri"
# APPLOI_API_KEY must hold the Partner API key you received from Apploi.
# These examples do not set it for you: set it yourself, in your own
# environment, before running them. For example, in your shell:
# export APPLOI_API_KEY="<the API key you received from Apploi>"
API_KEY = ENV.fetch("APPLOI_API_KEY")
def submit_application(body)
uri = URI("https://partners.apploi.com/easy-apply")
request = Net::HTTP::Post.new(uri)
request["X-Api-Key"] = API_KEY
request["Content-Type"] = "application/json"
request.body = JSON.generate(body)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, read_timeout: 60) do |http|
http.request(request)
end
case response.code
when "200" then [:submitted, JSON.parse(response.body)]
# The applicant has already applied to this job. Don't retry.
when "409" then [:already_applied, JSON.parse(response.body)]
# Only sent when "enqueue" is false. Don't retry.
when "412"
result = JSON.parse(response.body)
# With errors.job_id, the job is closed or doesn't exist. Otherwise the
# application already exists, for example from an earlier attempt.
(result["errors"] || {}).key?("job_id") ? [:job_closed, result] : [:already_applied, result]
# 400: fix the request before sending it again. 5xx: retry with backoff.
else raise "Request failed with status #{response.code}: #{response.body}"
end
end
def answer_for(question)
# Placeholder answers: replace them with the applicant's real answers.
id = question["id"].to_s # ids are sent as strings
case question["type"]
when "select" then { id: id, value: question["options"][0]["value"].to_s }
when "multiselect" then { id: id, values: question["options"].first(1).map { |option| option["value"].to_s } }
when "date" then { id: id, value: "21/04/1990" } # DD/MM/YYYY
# "format": "integer": a whole number from "min" to "max", as a string
when "text" then { id: id, value: question["min"].to_s }
else { id: id, value: "Sample answer" } # textarea
end
end
# 1. Look up the job, and check that it takes Full Apply.
uri = URI("https://partners.apploi.com/jobs/search")
uri.query = URI.encode_www_form(job_id: "468003", source: "your_partner_name")
request = Net::HTTP::Get.new(uri)
request["X-Api-Key"] = API_KEY
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, read_timeout: 30) do |http|
http.request(request)
end
raise "Request failed with status #{response.code}" unless response.is_a?(Net::HTTPSuccess)
job = JSON.parse(response.body)["data"].first
raise "Job 468003 was not found" if job.nil?
# The employer takes applications on its own site.
raise "Send the applicant to #{job["redirect_apply_url"]}" if job["external_url"]
raise "Job 468003 uses #{job["apply_method"]}, not Full Apply" unless job["apply_method"] == "full_apply_endpoint"
# 2. Get the job's questions. Fetching questions_url does not need your API key.
response = Net::HTTP.get_response(URI(job["questions_url"]))
raise "Request failed with status #{response.code}" unless response.is_a?(Net::HTTPSuccess)
questions = JSON.parse(response.body)["questions_en"]
# 3. Send the application with the answers.
body = {
applicant: {
email: "[email protected]",
fullName: "Jane Doe",
phoneNumber: "555-010-0123"
},
job: { jobId: job["id"] }, # a string, not a number
partner_attributes: job["partner_attributes"] # sent back unchanged
}
answers = questions.map { |question| answer_for(question) }
body[:questions] = { answers: answers } unless answers.empty?
outcome, result = submit_application(body)
puts outcome, resultExample Response
{
"developer_message": "Submit Successful",
"errors": null,
"info": {
"person_id": 32424609,
"bearer_token": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MzI0MjQ2MDksInJvbGVzIjpbImFwcGxpY2FudCJdLCJleHAiOjE2MTMxNTgxMjF9.vQuM-HDaPliAW53RO0f99Ti9px6dOnIY_nBJAU4F-K0",
"job_id": "598551"
}
}
Status Code Response
- 200 (Successful): The application was accepted. With the default
"enqueue": true, a job that has closed also returns200, and the application is then dropped. - 409 (Conflict): The applicant has already applied to this job, matched on email address. This happens unless the team has repeat applications turned on.
- 412 (Precondition Failed): Only sent when
"enqueue"isfalse. Iferrorshas ajob_id, the job is closed or doesn't exist; otherwise the application already exists. - 400 (Bad Request): The request or one of its answers is invalid.
developer_messagesays what's wrong.
See Errors for the other status codes.
Updated 13 days ago

