| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
This endpoint submits a job application to Apploi. It serves both API apply flows:
- Quick Apply, for jobs whose
apply_methodin the /jobs/search response iseasy_apply_endpoint. The job has no screening questions. - Full Apply, for jobs whose
apply_methodisfull_apply_endpoint. The application also carries the applicant's answers to the job's questions.
Jobs whose apply_method is redirect can't be applied to through the API.
Examples
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.
Quick Apply
A Quick Apply request needs three things:
- the applicant's
emailandfullName, - the job's id, as a string, in
job.jobId, - the job's
partner_attributesfrom the/jobs/searchresponse, sent back unchanged.
phoneNumber is optional. Leave out get_person_token, because the bearer token it returns belongs to the deprecated bearer-token flow. To attach a resume, add applicant.resume.file, as described in the body schema on this page.
The examples define a submit_application function (submitApplication in TypeScript), which the later examples reuse. It sorts each response into the outcomes described under What the response means.
# 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>"
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": "468036"
},
"partner_attributes": {
"sponsored": true,
"search_fetch_id": "394f44dc0aff4ee6ae469221e8934ee8",
"page": 1,
"order": 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}")
outcome, result = submit_application({
"applicant": {
"email": "[email protected]",
"fullName": "Jane Doe",
"phoneNumber": "555-010-0123",
},
"job": {"jobId": "468036"}, # a string, not a number
# Copied unchanged from the job's /jobs/search result
"partner_attributes": {
"sponsored": True,
"search_fetch_id": "394f44dc0aff4ee6ae469221e8934ee8",
"page": 1,
"order": 2,
},
})
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()}`);
}
const { outcome, result } = await submitApplication({
applicant: {
email: "[email protected]",
fullName: "Jane Doe",
phoneNumber: "555-010-0123",
},
job: { jobId: "468036" }, // a string, not a number
// Copied unchanged from the job's /jobs/search result
partner_attributes: {
sponsored: true,
search_fetch_id: "394f44dc0aff4ee6ae469221e8934ee8",
page: 1,
order: 2,
},
});
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
outcome, result = submit_application(
applicant: {
email: "[email protected]",
fullName: "Jane Doe",
phoneNumber: "555-010-0123"
},
job: { jobId: "468036" }, # a string, not a number
# Copied unchanged from the job's /jobs/search result
partner_attributes: {
sponsored: true,
search_fetch_id: "394f44dc0aff4ee6ae469221e8934ee8",
page: 1,
order: 2
}
)
puts outcome, resultWhat the response means
Decide what happened from the status code, not from the body. A successful response also contains a developer_message.
| Status | Meaning | What to do |
|---|---|---|
200 | The request was accepted. | Nothing. A 200 doesn't always mean an application was created. See Choosing when the application is processed for how this works by default. A developer_message of Application skipped due to inactive candidate. means the applicant's Apploi account is inactive, and no application was created. |
409 | The applicant has already applied to this job. Applications are matched on email address. | Treat it as "already applied", not as a failure. Don't retry. |
412 | Only sent when enqueue is false. If errors has a job_id, the job is closed or doesn't exist. Otherwise the application already exists, usually because an earlier attempt to send it is still being processed. | With job_id, remove the job from your listings. Otherwise, treat it like a 409. Don't retry in either case. |
400 | The request is invalid, for example a missing email or an answer that doesn't fit its question. developer_message says what's wrong. | Fix the request before sending it again. An unchanged retry fails the same way. |
5xx | Something failed on Apploi's side, or the request took longer than 29 seconds. | Wait, then retry with exponential backoff. |
A retry after a 5xx is safe: Apploi doesn't create a second application for the same applicant and job. The exception is a team that has repeat applications turned on (they're off by default). The retry may return 200 or 409, or, with enqueue: false, a 412 without a job_id.
A 200 response looks like this:
{
"developer_message": "Submit Successful",
"errors": null,
"info": {
"person_id": 32424609,
"job_id": "468036"
}
}A 409 response includes the id of the existing application:
{
"developerMessage": "This application has already been submitted.",
"errors": {
"application_form_id": 918273
}
}A 412 for a closed or missing job names the job. A 412 for an application that already exists has saved_application_id in errors instead.
{
"developerMessage": "Specified job doesn't exist or isn't accepting applications.",
"errors": {
"job_id": 468036
}
}Choosing when the application is processed
By default, enqueue is true. Apploi checks the request, replies, and creates the application a moment later. The response is quick, but a 200 only means the request was accepted, not that the application was created. If the job has closed, or doesn't exist, you still get 200, and the application is then dropped without notice.
Send "enqueue": false when you need to know straight away that the job is closed, for example to tell the applicant. Apploi then creates the application before replying, so the request takes longer, and a closed or missing job returns 412. If part of the application, such as the resume, can't be saved, the response is still 200, and errors lists what wasn't saved.
Some problems are reported straight away whichever value you send:
- a request that isn't valid returns
400, - a repeat application returns
409.
These examples reuse submit_application (Python and Ruby) and submitApplication (TypeScript) from the Quick Apply example above.
# 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>"
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 '{
"enqueue": false,
"applicant": {
"email": "[email protected]",
"fullName": "Jane Doe"
},
"job": {
"jobId": "468036"
},
"partner_attributes": {
"sponsored": true,
"search_fetch_id": "394f44dc0aff4ee6ae469221e8934ee8",
"page": 1,
"order": 2
}
}'outcome, result = submit_application({
"enqueue": False,
"applicant": {"email": "[email protected]", "fullName": "Jane Doe"},
"job": {"jobId": "468036"},
"partner_attributes": {
"sponsored": True,
"search_fetch_id": "394f44dc0aff4ee6ae469221e8934ee8",
"page": 1,
"order": 2,
},
})
if outcome == "job_closed":
print("This job is no longer accepting applications.")const { outcome } = await submitApplication({
enqueue: false,
applicant: { email: "[email protected]", fullName: "Jane Doe" },
job: { jobId: "468036" },
partner_attributes: {
sponsored: true,
search_fetch_id: "394f44dc0aff4ee6ae469221e8934ee8",
page: 1,
order: 2,
},
});
if (outcome === "job_closed") {
console.log("This job is no longer accepting applications.");
}outcome, _result = submit_application(
enqueue: false,
applicant: { email: "[email protected]", fullName: "Jane Doe" },
job: { jobId: "468036" },
partner_attributes: {
sponsored: true,
search_fetch_id: "394f44dc0aff4ee6ae469221e8934ee8",
page: 1,
order: 2
}
)
puts "This job is no longer accepting applications." if outcome == :job_closedFull Apply
A Full Apply request is a Quick Apply request with the applicant's answers added in questions.answers. Get the job's questions from the questions_url in its /jobs/search result. The Full Apply guide describes that response.
Formatting questions.answers
questions.answersEach answer has the question's id and exactly one of value or values, and nothing else. Send every id and every value as a string, even when it is a number. The answer's form depends on the question's type:
Question type | Answer | Example |
|---|---|---|
textarea | value: any text | {"id": "1893206", "value": "Yes, license LPC-80421"} |
text, with "format": "integer" | value: a whole number from 0 to 100, as a string | {"id": "1877310", "value": "5"} |
select | value: the chosen option's value | {"id": "1854409", "value": "1"} |
multiselect | values: a list of the chosen options' values | {"id": "1841349", "values": ["0", "2"]} |
date | value: a date in DD/MM/YYYY format | {"id": "1920662", "value": "21/04/1990"} |
An option's value is its position in the question's options list, counting from 0. For example, with these options:
{
"options": [{"value": 0, "label": "Yes"}, {"value": 1, "label": "No"}]
}the answer "No" is sent as "value": "1". A multiselect question takes values (plural); using value with a list returns 400.
Each answer is checked against the job's questions. An id that isn't one of the job's questions, an option that doesn't exist, or a date in another format returns 400, with a developer_message that explains the problem.
The examples below use the example questions from the Full Apply guide.
# 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>"
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"] }
]
}
}'outcome, result = submit_application({
"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"}, # textarea
{"id": "1920662", "value": "21/04/1990"}, # date, DD/MM/YYYY
{"id": "1854409", "value": "1"}, # select: option 1, "No"
{"id": "1841349", "values": ["0", "2"]}, # multiselect: options 0 and 2
]
},
})
print(outcome, result)const { outcome, result } = await submitApplication({
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" }, // textarea
{ id: "1920662", value: "21/04/1990" }, // date, DD/MM/YYYY
{ id: "1854409", value: "1" }, // select: option 1, "No"
{ id: "1841349", values: ["0", "2"] }, // multiselect: options 0 and 2
],
},
});
console.log(outcome, result);outcome, result = submit_application(
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" }, # textarea
{ id: "1920662", value: "21/04/1990" }, # date, DD/MM/YYYY
{ id: "1854409", value: "1" }, # select: option 1, "No"
{ id: "1841349", values: ["0", "2"] } # multiselect: options 0 and 2
]
}
)
puts outcome, result 409Duplicate application: We define duplicate as an application from the same job seeker email, to the same jobid
410If a job post is expired or no longer available.
503request took longer than 30 seconds to complete.

