| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Here is the documentation to use this endpoint as library.
For the full workflow around job applications (finding them, updating their status, messaging applicants and scheduling interviews), see the Applicant & Status Management guide.
Examples
Each example reads the job applications for one team. Replace 123456 with one of your team ids.
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.
Your first request
Ask for one small page of job applications:
# 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 -G https://partners.apploi.com/applicants \
-H "X-Api-Key: $APPLOI_API_KEY" \
--data-urlencode "team_id=123456" \
--data-urlencode "limit=50"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"]
response = requests.get(
"https://partners.apploi.com/applicants",
headers={"X-Api-Key": API_KEY},
params={"team_id": 123456, "limit": 50},
timeout=60,
)
response.raise_for_status()
page = response.json()
for job_application in page["data"]:
print(job_application["id"], job_application["job_id"], job_application["applicant_updated"])// 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;
const params = new URLSearchParams({ team_id: "123456", limit: "50" });
const response = await fetch(
`https://partners.apploi.com/applicants?${params}`,
{
headers: { "X-Api-Key": API_KEY },
},
);
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
const page = await response.json();
for (const jobApplication of page.data) {
console.log(
jobApplication.id,
jobApplication.job_id,
jobApplication.applicant_updated,
);
}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")
uri = URI("https://partners.apploi.com/applicants")
uri.query = URI.encode_www_form(team_id: 123456, limit: 50)
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: 60) do |http|
http.request(request)
end
raise "Request failed with status #{response.code}" unless response.is_a?(Net::HTTPSuccess)
page = JSON.parse(response.body)
page["data"].each do |job_application|
puts [job_application["id"], job_application["job_id"], job_application["applicant_updated"]].join(" ")
endThe response wraps the job applications in data and echoes the offset and limit that were applied. Trimmed to a few fields, it looks like this:
{
"data": [
{
"id": 98765432,
"applicant_id": 1234567,
"job_id": 7654321,
"applicant_updated": "2026-09-18T14:03:22+00:00"
}
],
"offset": 0,
"limit": 50
}Each entry is one job application: one person's application to one job. id identifies the job application and applicant_id identifies the person who applied, so a person who applied to two jobs appears twice with the same applicant_id.
Paging through job applications
Results come back with the most recent job applications first. They are ordered by application date, not by applicant_updated. To read more than one page, add limit to offset on each request, and stop when a page holds fewer job applications than the limit in the response.
Compare against the response's limit rather than the one you sent. The largest page is 100, and a larger limit is served as 100 rather than rejected, so a loop that compares against a requested 500 would stop after the first page. When that happens the response also carries a warning naming the maximum. Don't rely on a total field to know when to stop either: it isn't always present.
The results can change while you page:
- When a job application joins the results, later rows move down one place, so one row can appear on two pages. The examples keep one copy of each job application by
id. - When a job application leaves the results, later rows move up one place, so one row can be skipped.
To catch anything skipped, run a full read from time to time as well as the incremental reads described below.
# 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>"
# Page 1
curl -G https://partners.apploi.com/applicants \
-H "X-Api-Key: $APPLOI_API_KEY" \
--data-urlencode "team_id=123456" \
--data-urlencode "limit=50" \
--data-urlencode "offset=0"
# Page 2: offset increases by limit
curl -G https://partners.apploi.com/applicants \
-H "X-Api-Key: $APPLOI_API_KEY" \
--data-urlencode "team_id=123456" \
--data-urlencode "limit=50" \
--data-urlencode "offset=50"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 fetch_all_job_applications(team_id, params=None):
job_applications = {}
offset = 0
while True:
response = requests.get(
"https://partners.apploi.com/applicants",
headers={"X-Api-Key": API_KEY},
params={"team_id": team_id, "limit": 50, "offset": offset, **(params or {})},
timeout=60,
)
response.raise_for_status()
page = response.json()
for job_application in page["data"]:
job_applications[job_application["id"]] = job_application
if len(page["data"]) < page["limit"]:
return list(job_applications.values())
offset += page["limit"]
job_applications = fetch_all_job_applications(123456)// 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;
async function fetchAllJobApplications(
teamId: string,
extraParams: Record<string, string> = {},
): Promise<any[]> {
const jobApplications = new Map<number, any>();
let offset = 0;
while (true) {
const params = new URLSearchParams({
team_id: teamId,
limit: "50",
offset: String(offset),
...extraParams,
});
const response = await fetch(
`https://partners.apploi.com/applicants?${params}`,
{
headers: { "X-Api-Key": API_KEY },
},
);
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
const page = await response.json();
for (const jobApplication of page.data) {
jobApplications.set(jobApplication.id, jobApplication);
}
if (page.data.length < page.limit) {
return [...jobApplications.values()];
}
offset += page.limit;
}
}
const jobApplications = await fetchAllJobApplications("123456");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 fetch_all_job_applications(team_id, params = {})
job_applications = {}
offset = 0
loop do
uri = URI("https://partners.apploi.com/applicants")
uri.query = URI.encode_www_form({ team_id: team_id, limit: 50, offset: offset }.merge(params))
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: 60) do |http|
http.request(request)
end
raise "Request failed with status #{response.code}" unless response.is_a?(Net::HTTPSuccess)
page = JSON.parse(response.body)
page["data"].each do |job_application|
job_applications[job_application["id"]] = job_application
end
return job_applications.values if page["data"].length < page["limit"]
offset += page["limit"]
end
end
job_applications = fetch_all_job_applications(123456)Pulling only what changed
After the first full read, sync on a schedule and ask only for job applications updated since your last successful run with updated_after. This is much faster than reading everything again.
Send times in UTC. A timezone offset is accepted but not applied, so 10:00:00-05:00 is read as 10:00:00Z. Record the time a run starts, minus a few minutes, and save it as the next run's updated_after only after the run finishes without errors. Anything updated while the run was in progress is then picked up next time. The few minutes cover the case where your clock is ahead of Apploi's: without them, job applications updated in that gap would never be returned. The filter includes its boundary, so a few job applications may come back twice across runs. Update them by id rather than inserting them again.
These examples reuse fetch_all_job_applications (Python and Ruby) / fetchAllJobApplications (TypeScript) from the paging 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 -G https://partners.apploi.com/applicants \
-H "X-Api-Key: $APPLOI_API_KEY" \
--data-urlencode "team_id=123456" \
--data-urlencode "limit=50" \
--data-urlencode "offset=0" \
--data-urlencode "updated_after=2026-09-01T00:00:00Z"from datetime import datetime, timedelta, timezone
def sync_changed_job_applications(team_id, last_run_started):
# A few minutes earlier than now, in case this clock is ahead of Apploi's
run_started = (datetime.now(timezone.utc) - timedelta(minutes=5)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
)
changed_job_applications = fetch_all_job_applications(
team_id, {"updated_after": last_run_started}
)
# Store the changed job applications, updating any you already have by "id"...
return run_started # save this as last_run_started for the next run
next_checkpoint = sync_changed_job_applications(123456, "2026-09-01T00:00:00Z")async function syncChangedJobApplications(
teamId: string,
lastRunStarted: string,
): Promise<string> {
// A few minutes earlier than now, in case this clock is ahead of Apploi's
const runStarted = new Date(Date.now() - 5 * 60 * 1000)
.toISOString()
.replace(/\.\d{3}Z$/, "Z");
const changedJobApplications = await fetchAllJobApplications(teamId, {
updated_after: lastRunStarted,
});
// Store the changed job applications, updating any you already have by "id"...
return runStarted; // save this as lastRunStarted for the next run
}
const nextCheckpoint = await syncChangedJobApplications(
"123456",
"2026-09-01T00:00:00Z",
);def sync_changed_job_applications(team_id, last_run_started)
# A few minutes earlier than now, in case this clock is ahead of Apploi's
run_started = (Time.now.utc - 5 * 60).strftime("%Y-%m-%dT%H:%M:%SZ")
changed_job_applications = fetch_all_job_applications(team_id, { updated_after: last_run_started })
# Store the changed job applications, updating any you already have by "id"...
run_started # save this as last_run_started for the next run
end
next_checkpoint = sync_changed_job_applications(123456, "2026-09-01T00:00:00Z")Keeping requests fast
A request that takes more than 29 seconds is cut off with a 504 error. Large, unfiltered reads are the usual cause. To stay well inside the limit:
- Always send
team_id, and addjob_idwhen you only need one job. - Use a page size of around 50 rather than the maximum.
- Use
updated_afterfor regular syncs instead of reading every job application each time. - If a request returns
504or another5xxerror, wait a few seconds and retry the same page.

