API Reference
Job Search
Search for jobs across supported ATS platforms by title, location, and filters. Every returned job can be applied to via the Sessions endpoint.
Overview
The Job Search endpoint lets you find open positions across all supported ATS platforms. Results are pre-filtered to only include jobs that can be submitted through the API — so every job you get back is ready to be passed directly to the Sessions apply endpoint.
This is useful for clients who don't have their own job feed and want to offer end-to-end search-and-apply functionality through a single API.
Search Jobs
Search for jobs by title, location, and optional filters. Returns a list of matching jobs with full details including company information, salary data, and direct application URLs.
/jobs/search| Name | Type | Required | Description |
|---|---|---|---|
titles | string[] | Required | Array of job titles to search for (e.g. ["Software Engineer", "Frontend Developer"]) |
locations | string[] | Optional | Array of locations (e.g. ["Remote", "United States", "San Francisco, CA"]) |
postedAtMaxAgeDays | integer | Optional | Maximum age of job postings in days (default: 30) |
results | integer | Optional | Target number of results to return (default: 50, max: 500) |
excludeJobIds | string[] | Optional | Array of job IDs to exclude from results (e.g. previously applied jobs) |
userId | string | Optional | User ID for user-level deduplication. When provided, repeated searches only exclude jobs previously returned to this specific user. Without it, deduplication is scoped to the entire client/API key. |
filtersobjectOptional| Field | Type | Required | Description |
|---|---|---|---|
workType | string | string[] | Optional | Work arrangement filter. Accepts a single value or array: "Remote", "On-site", or "Hybrid". Hybrid is treated as non-remote for filtering purposes. |
jobType | string | string[] | Optional | Employment type filter. Accepts a single value or array: "full-time", "part-time", "contract", or "internship" |
experienceLevel | string | string[] | Optional | Seniority filter. Accepts a single value or array: "junior", "mid", "senior", "lead", or "executive" |
salaryMin | integer | Optional | Minimum annual salary in USD (e.g. 100000) |
companySizeMin | integer | Optional | Minimum company employee count (e.g. 50) |
curl -X POST https://apply-api.boringproject.ai/api/v1/jobs/search \
-H "Authorization: Bearer bp_live_..." \
-H "Content-Type: application/json" \
-d '{
"titles": ["Software Engineer", "Full Stack Developer"],
"locations": ["Remote", "San Francisco, CA"],
"userId": "usr_abc123",
"results": 25,
"filters": {
"workType": ["Remote", "Hybrid"],
"experienceLevel": ["mid", "senior"],
"salaryMin": 100000
}
}'{
"searchId": "srch_abc123",
"query": "Software Engineer, Full Stack Developer",
"resultsCount": 25,
"jobs": [
{
"jobId": "48291",
"title": "Senior Software Engineer",
"companyName": "Acme Corp",
"url": "https://boards.greenhouse.io/acme/jobs/48291",
"jobBoard": "Greenhouse",
"description": "We are looking for a Senior Software Engineer to join our platform team...",
"datePosted": "2024-02-10",
"location": "Remote, US",
"salary": "$140,000 - $180,000",
"salaryMin": 140000,
"salaryMax": 180000,
"salaryCurrency": "USD",
"jobType": "full-time",
"remote": true,
"seniority": "senior",
"companyDomain": "acmecorp.com",
"logoUrl": "https://logo.clearbit.com/acmecorp.com",
"companySize": 500,
"companyIndustry": "Technology"
},
...
]
}Job Object Fields
Each job in the response array contains the following fields. Fields marked Always are guaranteed to be present. Fields marked Nullable may be null when data is not available.
| Name | Type | Presence | Description |
|---|---|---|---|
jobId | string | Always | Unique job identifier |
title | string | Always | Job title |
companyName | string | Always | Hiring company name |
url | string | Always | Direct application URL — pass this as the link field in POST /sessions/apply |
jobBoard | string | Always | ATS platform name (e.g. Greenhouse, Lever, Ashby) |
description | string | null | Nullable | Full job description text (may be null if source doesn't provide it) |
datePosted | string | null | Nullable | Date the job was posted, YYYY-MM-DD (may be null) |
location | string | null | Nullable | Job location, e.g. "Remote, US" (may be null) |
remote | boolean | Always | Whether the job is remote |
salary | string | null | Nullable | Formatted salary string (e.g. "$140,000 - $180,000") |
salaryMin | number | null | Nullable | Minimum annual salary in USD |
salaryMax | number | null | Nullable | Maximum annual salary in USD |
salaryCurrency | string | null | Nullable | Salary currency code |
jobType | string | Nullable | Employment type: full-time, part-time, contract, or internship |
seniority | string | Nullable | Seniority level of the role |
companyDomain | string | Nullable | Company website domain |
logoUrl | string | null | Nullable | Company logo URL |
companySize | integer | null | Nullable | Company employee count |
companyIndustry | string | Nullable | Company industry |
Search + Apply Flow
The typical workflow is to search for jobs, let the user select which ones to apply to, and then pass them to the Sessions apply endpoint. The job url field maps directly to the link field in the apply request.
# 1. Search for jobs
curl -X POST https://apply-api.boringproject.ai/api/v1/jobs/search \
-H "Authorization: Bearer bp_live_..." \
-H "Content-Type: application/json" \
-d '{"titles": ["Software Engineer"], "locations": ["Remote"]}'
# 2. Apply to selected jobs (using job URLs from search results)
curl -X POST https://apply-api.boringproject.ai/api/v1/sessions/apply \
-H "Authorization: Bearer bp_live_..." \
-H "Content-Type: application/json" \
-d '{
"candidateProfileId": "prof_xyz789",
"jobs": [
{ "companyName": "Acme Corp", "title": "Senior Software Engineer", "jobId": "48291", "link": "https://boards.greenhouse.io/acme/jobs/48291" }
]
}'Automatic Deduplication
The API automatically excludes jobs that were returned in previous searches for your account within the last 24 hours. Each search only returns fresh, never-before-seen jobs — so you can call the search endpoint repeatedly without worrying about duplicates.
This works by tracking the job IDs returned in your recent search records (within a 24-hour rolling window). When a new search is performed, those job IDs are excluded server-side so results are always fresh.
For Autopilot Recurring sessions, deduplication is even more specific — it filters out jobs that the candidate has already applied to (regardless of success or failure), ensuring each recurring run only applies to genuinely new positions.
Filter Behavior
Filter parameters (workType, jobType, experienceLevel) accept either a single string or an array of strings. When multiple values are provided, results matching any of the specified values are returned.
If fewer jobs match your filters than the requested results count, the API progressively relaxes filters to find more results. The relaxation order is: salary minimum and company size are dropped first, then experience level and job type, and finally work type. This ensures you always get the most relevant results possible while still meeting your target count.
Note: Hybrid work type is treated as non-remote for filtering purposes, since the underlying job data only distinguishes between fully remote and non-remote positions.
List Past Searches
Retrieve a paginated list of your previous job searches. Each record includes the search query, filters used, result count, and how many applications were submitted from that search.
/searches| Name | Type | Required | Description |
|---|---|---|---|
page | integer | Optional | Page number (default: 1) |
limit | integer | Optional | Items per page, max 100 (default: 20) |
curl "https://apply-api.boringproject.ai/api/v1/searches?page=1&limit=20" \
-H "Authorization: Bearer bp_live_..."{
"data": [
{
"searchId": "srch_abc123",
"query": "Software Engineer, Full Stack Developer",
"titles": ["Software Engineer", "Full Stack Developer"],
"locations": ["Remote", "San Francisco, CA"],
"filters": { "workType": ["Remote"], "experienceLevel": ["mid", "senior"] },
"resultsCount": 25,
"totalAvailable": 2413,
"totalCompanies": 1208,
"appliedCount": 5,
"createdAt": "2024-02-14T10:30:00Z"
}
],
"pagination": { "page": 1, "limit": 20, "total": 12, "totalPages": 1 }
}Get Search Record
Retrieve a specific search record by ID, including the full jobs array. Use this to show the original search results or to let users select jobs to apply to from a previous search.
/searches/:searchIdcurl "https://apply-api.boringproject.ai/api/v1/searches/srch_abc123" \
-H "Authorization: Bearer bp_live_..."{
"searchId": "srch_abc123",
"query": "Software Engineer",
"titles": ["Software Engineer"],
"locations": ["Remote"],
"filters": {},
"resultsCount": 25,
"totalAvailable": 2413,
"totalCompanies": 1208,
"appliedCount": 5,
"jobs": [
{
"jobId": "48291",
"title": "Senior Software Engineer",
"companyName": "Acme Corp",
"url": "https://boards.greenhouse.io/acme/jobs/48291",
"jobBoard": "Greenhouse",
"location": "Remote, US",
"salary": "$140,000 - $180,000",
"remote": true
}
],
"createdAt": "2024-02-14T10:30:00Z"
}Rate Limiting
Job Search requests count toward your API key's 25 concurrent request limit (shared with Sessions). Each search holds a concurrent slot until the results are fully returned. If you exceed 25 simultaneous in-flight requests, additional requests will receive a 429 response.
For high-volume search use cases, stagger requests or wait for previous searches to complete before sending new ones. See the Rate Limiting guide for details on response headers and retry strategies.
Related docs
Continue reading
Sessions
Create and manage job application sessions — apply to specific jobs, run one-time searches, or set up recurring autopilot campaigns.
Candidate Profiles
Manage candidate profiles containing personal information, work experience, education, skills, languages, certifications, achievements, miscellaneous details, and resumes.
Rate Limiting
Understand API rate limits, response headers, and how to handle 429 responses gracefully with exponential backoff.