The short answer
JSearch returns job data and application links. To build a product that submits applications, add an explicit candidate-authorized workflow and check whether each employer URL is supported.
Start with the provider's search endpoint
OpenWeb Ninja documents JSearch as a listings and salary-data API. Its direct API example uses search-v2 with an x-api-key header. Keep the key on your backend. The request below is a discovery example; it does not submit an application or connect to Apply Guy.
curl --fail-with-body --get 'https://api.openwebninja.com/jsearch/search-v2' \
--header "x-api-key: $JSEARCH_API_KEY" \
--data-urlencode 'query=software engineer in chicago'Keep source identity and employer identity separate
A listing can contain a publisher link, an employer application link, or several apply options. Preserve the original source record, the link you selected, and when you retrieved it. Do not treat a JSearch job ID as an Apply Guy catalog ID.
Before offering an apply action, verify location, employer, title, and whether the target is an actual employer application page. An aggregator link may redirect, expire, or lead to a platform your submission provider does not support. A matching title alone is not enough to merge records.
Preflight a supported employer URL
Apply Guy accepts either its own catalog jobId or a supported employer jobUrl, not both in the same request. For an external listing, pass the verified employer URL with the candidate's identity and chosen documents to preflight. Inspect readiness, enabled ATS support, missing profile details, and email-integration requirements.
Apply Guy's current developer workflow is scoped to US jobs and US-normalized profiles. A global discovery source does not expand that submission scope. A ready preflight is also not a guarantee that an employer will accept a later submission: the live form and job availability can change.
import { ApplyGuy } from "@applyguy/sdk";
const apiKey = process.env.APPLYGUY_API_KEY;
const candidateId = process.env.CANDIDATE_ID;
const jobUrl = process.env.EMPLOYER_JOB_URL;
if (!apiKey || !candidateId || !jobUrl) {
throw new Error("Set APPLYGUY_API_KEY, CANDIDATE_ID and EMPLOYER_JOB_URL");
}
const client = new ApplyGuy({ apiKey });
const { data } = await client.applications.preflight({
candidateId,
jobUrl,
mode: "managed",
});
console.log({ ready: data.ready, missing: data.missingProfileFields,
emailRequired: data.emailIntegrationRequired, cost: data.cost });Make submission a separate, authorized action
Show the candidate the opening and the documents being used before creating an application. Store one stable idempotency key for that intended candidate-and-job operation. A network timeout should lead to retrieving or retrying the same operation, not generating another key and creating a second submission.
Persist the returned application ID. Queued or running is not employer acceptance. Read the terminal status or handle signed webhook events, and support an answer round when using agent mode. Do not claim an interview outcome from a submission result.
Compare products using the same task
If you need listings for a global search interface, evaluate discovery coverage and freshness. If you need a US submission workflow, also evaluate supported employer URLs, candidate isolation, documents, verification, and outcomes. Compare current provider documentation and your own sample rather than assuming equal coverage.
This guide is written by Apply Guy, a commercial provider. It is not an independent benchmark, and the examples do not imply that every JSearch result is supported by Apply Guy.
Sources and further reading
Provider and employer requirements can change. Follow the instructions for the particular application or integration.