Build on the ApplyLoop jobs feed.
The ApplyLoop API exposes the same catalog our own product runs on: every posting our scout discovers across 26 ATS platforms (Greenhouse, Lever, Ashby, Workday, iCIMS, and 20+ more), deduplicated, freshness-validated (stale postings are pruned), and filtered to US locations.
It's built for job boards that want a fresh source, researchers studying the hiring market, and anyone wiring jobs into a personal tool or agent. One endpoint, bearer-token auth, keyset pagination.
Get an API key
- Create a free ApplyLoop account. No card required.
- In the dashboard, open API access.
- Click Create token and grant it the
jobs:readscope.
Authentication
Every request carries your key as a bearer token. Keys are per-account, start with alk_, and can be revoked at any time from the same API access page. Revocation takes effect immediately.
Authorization: Bearer alk_YOUR_TOKENCORS is open for GET requests, so the API is callable directly from browser apps too.
Jobs API
/api/v1/jobsReturns jobs newest-first (by discovered_at). Suppressed postings (captcha, login walls, closed roles) and platforms with a temporarily disabled apply flow are excluded. You only ever see actionable jobs.
Query parameters
All parameters are optional.
| Parameter | Description |
|---|---|
| ats | Comma-separated platform slugs to include, e.g. ats=greenhouse,lever. Omit for all platforms. |
| q | Case-insensitive substring match on job title or company name. |
| location | Case-insensitive substring match on location, e.g. location=remote. |
| level | Career-ladder bucket derived from the job title: intern, junior, mid, senior, or lead (lead = Lead and above). Unknown values are ignored. |
| category | Role family derived from the job title: data-scientist, data-analyst-eng, ai-ml, network-security, cybersecurity, cloud-devops, software-eng, other. Unknown values are ignored. |
| since | Epoch milliseconds: return only jobs discovered after this moment. Ideal as an incremental polling cursor. |
| limit | Page size, 1 to 100. Default 50. |
| cursor | Keyset pagination cursor: pass the nextCursor value from the previous page to fetch the next (older) page. |
Response
A JSON object: { jobs, nextCursor, count }.
{
"jobs": [
{
"id": "job_9f1c2ab4",
"ats": "greenhouse",
"title": "Machine Learning Engineer",
"company": "Acme AI",
"company_domain": "acme.ai",
"location": "Remote, US",
"apply_url": "https://boards.greenhouse.io/acmeai/jobs/4123456",
"posted_at": 1753180000000,
"discovered_at": 1753210000000
}
],
"nextCursor": 1753210000000,
"count": 1
}| Field | Type | Description |
|---|---|---|
| id | string | Stable job identifier. |
| ats | string | Source platform slug, e.g. greenhouse, lever, ashby. |
| title | string | Job title. |
| company | string | Company name. |
| company_domain | string | null | Company website domain, when known. |
| location | string | null | Location as posted (US-filtered, see note below). |
| apply_url | string | Direct link to the application page on the source ATS. |
| posted_at | number | null | Epoch ms when the role was posted, when the source ATS exposes it. |
| discovered_at | number | Epoch ms when our scout first saw the role. Sort key: results are newest-first. |
Pagination: pass nextCursor back as cursor to fetch the next page, and stop when it's null. A US-location filter is applied per page after fetching, so count can be lower than your limit while more pages still remain. Always paginate to the end rather than stopping at a short page.
Errors
| Status | Body | When |
|---|---|---|
| 401 | {"error":"unauthorized"} | Missing, invalid, or revoked token, or the token lacks the jobs:read scope. |
| 429 | {"error":"rate_limited","retryAfterSec":n} | Per-key requests-per-minute ceiling hit. The retry-after header carries the same value in seconds. |
| 429 | {"error":"too_many_concurrent_requests"} | Too many simultaneous in-flight requests on one key. Sent with retry-after: 1. |
| 503 | {"error":"jobs_api_not_enabled"} | Only on the shared service-key path when that key is not configured on the deployment. Personal alk_ tokens never see this. |
Requests sent with Accept: text/html (e.g. pasting the URL into a browser) are redirected to the dashboard's API access page instead of showing raw JSON errors.
Code examples
Replace alk_YOUR_TOKEN with your own key. The Python and JavaScript examples paginate through the full result set.
curl "https://applyloop.pro/api/v1/jobs?q=machine+learning&limit=50" \
-H "Authorization: Bearer alk_YOUR_TOKEN"import requests
BASE = "https://applyloop.pro/api/v1/jobs"
HEADERS = {"Authorization": "Bearer alk_YOUR_TOKEN"}
jobs, cursor = [], None
while True:
params = {"q": "machine learning", "limit": 100}
if cursor:
params["cursor"] = cursor
page = requests.get(BASE, headers=HEADERS, params=params).json()
jobs += page["jobs"]
cursor = page.get("nextCursor")
if not cursor:
break
print(f"{len(jobs)} jobs")const BASE = "https://applyloop.pro/api/v1/jobs";
const headers = { Authorization: "Bearer alk_YOUR_TOKEN" };
const jobs = [];
let cursor = null;
do {
const url = new URL(BASE);
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", String(cursor));
const page = await fetch(url, { headers }).then((r) => r.json());
jobs.push(...page.jobs);
cursor = page.nextCursor;
} while (cursor);
console.log(`${jobs.length} jobs`);Rate limits
Limits are enforced per key, so one consumer can never starve another. Current defaults:
| Limit | Default | Notes |
|---|---|---|
| Requests per minute | 60 | Fixed one-minute window, counted per key. |
| Concurrent requests | 4 | Maximum simultaneous in-flight requests per key. Parallel fan-out beyond this returns 429. |
Successful responses include an x-ratelimit-remaining header; when you exceed a limit you get a 429 with a retry-after header (see Errors above). These are fair-use defaults and may be tuned server-side. If you need more headroom, write to founders@applyloop.app.
Account endpoints
Personal tokens can carry more than jobs:read. These endpoints act on your own ApplyLoop account:
| Endpoint | Scope | What it does |
|---|---|---|
| GET /api/v1/me | any token | Your plan, quota and applications remaining. |
| GET /api/v1/matches | apply:read | Your pending matches (the same queue the dashboard shows). |
| GET /api/v1/applications | apply:read | Your applications with statuses and receipts. |
| POST /api/v1/apply | apply:write | Approve one match by queue id (identical to tapping Apply). |
Coming soon
The token scopes apply:read and apply:write already exist in the dashboard. The surfaces they unlock are in preview, and their docs will land here as they stabilize:
Your application pipeline over API: statuses, submissions, and outcomes.
Trigger applications programmatically: one role or the whole matched queue.
Drive ApplyLoop from agents: an MCP server plus chat surfaces built on the same tokens.