Agentic Search
Search job postings from a plain-language request with POST /v1/jobs/agentic-search.
POST https://api.jobspipe.dev/v1/jobs/agentic-searchTakes a plain-language request and returns the best-matching postings first, each with a relevance score. Requires authentication.
Use it when you have a sentence rather than filters: "senior backend engineer in Berlin, visa sponsorship, hybrid ok". When you already know the filters, /v1/jobs/search is faster and pages through the whole result set.
How it works
- Plan. The request becomes up to three structured searches (the titles employers post, the place, and one broader angle), hard rules on stored fields such as country, posted pay and posting date, and up to three questions to ask of each posting's text, such as "does the employer sponsor work visas?".
- Retrieve and check. The searches run against the same index as
/v1/jobs/search. Postings that break a hard rule are dropped. Unknown values pass: a posting with no salary is not "below the floor". - Score. Each candidate is scored against the role, level and field you asked for, and each text question is answered yes, no or not stated. Each question carries the answer you want (
want), so "not hospitals" becomes "Is the posting from a hospital?" withwant: "no". A hard question removes a posting only when its text gives the unwanted answer. Postings whose text tries to instruct an AI reader are never returned. - Widen if needed. If too few postings pass, new searches are tried, up to three rounds within about 25 seconds, always keeping the place you asked for. The top ten then get a stricter check on exact role and level, which can reorder them.
Postings scoring below 0.3 are never returned, so a request with nothing relevant returns fewer postings, or none. A posting whose role score (judgments.relevance) is between 0.3 and 0.7 carries uncertain: true; treat those as worth a human look. relevance can be lower for a clear match when a condition you asked about is not stated in the posting.
Near matches
A posting that matches the role you asked for but not every stated condition (the level, the arrangement, a question whose answer the text leaves open) is returned with near_match: true. metadata.agentic.near_matches counts them on the page and passed says how many candidates cleared the bar outright, so a page where passed is 0 and near_matches > 0 is made only of near matches. raw_passed tells how many candidates the judge itself rated relevant before the stated conditions were applied.
When nothing clears the bar at all, the closest candidates are graded once more against the request as a whole and the ones that are the right role and field come back with rescued: true (and near_match: true); their relevance is that grade on a 0 to 1 scale. metadata.agentic.rescue says whether this ran (ran), how many candidates it graded and returned, and failed: true when it could not run to completion, in which case the page stays empty. Rescued postings are billed like any other returned posting.
metadata.agentic shows the restated intent, each search and what it returned, the rules and questions, and why the search stopped.
Request
curl https://api.jobspipe.dev/v1/jobs/agentic-search \
-H "Authorization: Bearer jp_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"query": "senior backend engineer in Berlin, visa sponsorship, hybrid ok",
"filters": { "posted_at_max_age_days": 30 },
"limit": 10
}'import requests
resp = requests.post(
"https://api.jobspipe.dev/v1/jobs/agentic-search",
headers={"Authorization": "Bearer jp_live_your_key_here"},
json={
"query": "senior backend engineer in Berlin, visa sponsorship, hybrid ok",
"filters": {"posted_at_max_age_days": 30},
"limit": 10,
},
timeout=60,
)
resp.raise_for_status()
for job in resp.json()["data"]:
print(round(job["relevance"], 2), job["job_title"], "-", job["company"])const resp = await fetch("https://api.jobspipe.dev/v1/jobs/agentic-search", {
method: "POST",
headers: {
Authorization: "Bearer jp_live_your_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "senior backend engineer in Berlin, visa sponsorship, hybrid ok",
filters: { posted_at_max_age_days: 30 },
limit: 10,
}),
});
const result = await resp.json();
for (const job of result.data) {
console.log(job.relevance.toFixed(2), job.job_title, "-", job.company);
}| Field | Type | Description |
|---|---|---|
query | string | Required. 2 to 500 characters of plain language. |
filters | object | Optional hard filters applied to every planned search, in the names of the filter reference: job_country_code_or, city_or, region_or, remote, posted_at_max_age_days, language_or and the rest. Unknown keys are ignored; paging and ordering keys are not accepted. |
limit | integer | Postings to return, 1 to 25 (default 10), never more than your plan's page size. |
Unknown top-level keys are rejected with 400.
Response
metadata: what the call cost (credits_charged,jobs_already_paid), what the account can still spend (credits_remainingofcredits_allowance), andagentic, the plan and how it ran.total_resultsandnext_cursorare alwaysnull: there are no further pages.data: the postings, highestrelevancefirst. Each is a job object with six extra fields:relevance(0 to 1),uncertain(role score between 0.3 and 0.7),near_match(matched the request but not every stated condition),rescued(one of the closest matches, returned because nothing cleared the bar),judgments(the scores behind it, includinginjectionand, for the top ten,recheck) andlanes(which planned searches returned it).
{
"metadata": {
"total_results": null,
"truncated_results": 0,
"next_cursor": null,
"credits_charged": 8,
"jobs_already_paid": 2,
"credits_remaining": 24382,
"credits_allowance": 25000,
"agentic": {
"intent": "Senior backend engineer in Berlin, hybrid acceptable, with visa sponsorship.",
"role_intent": "Senior backend engineer",
"rounds": 1,
"stop": "enough candidates passed",
"candidates": 96,
"dropped": 0,
"passed": 31,
"fallback_used": false,
"lanes": [
{ "round": 1, "name": "Senior backend Berlin", "filters": { "job_title_or": ["senior backend engineer", "senior backend developer"], "city_or": ["Berlin"], "job_country_code_or": ["DE"] }, "returned": 50, "relevant": 27, "ms": 1840 }
],
"judge": [{ "key": "visa", "question": "Does the posting say the employer sponsors work visas?", "want": "yes", "hard": false }],
"verify": { "country_codes": ["DE"] },
"jev_calls": 3,
"jev_failed_batches": 0,
"near_matches": 0,
"raw_passed": 31,
"rescue": { "ran": false, "candidates": 0, "returned": 0, "ms": 0, "failed": false },
"timings": { "planner_ms": 3900, "search_ms": 2100, "jev_ms": 1200, "total_ms": 7300 }
}
},
"data": [
{
"id": "8164933",
"job_title": "Senior Backend Engineer (Go)",
"company": "Northwind Labs",
"location": "Berlin, Germany",
"country_code": "DE",
"relevance": 0.91,
"uncertain": false,
"near_match": false,
"rescued": false,
"judgments": { "relevance": 0.96, "constraints": { "visa": { "yes": 0.82, "no": 0.02, "not_stated": 0.16 } } },
"lanes": ["Senior backend Berlin"]
}
]
}relevance is null on every posting when scoring was unavailable; the page is then newest first, and metadata.agentic.jev_failed_batches (failed scoring batches) is above zero. fallback_used: true means planning was unavailable and the request was searched as a job title instead.
Cost and limits
- One credit per posting returned that your account has not already paid for this calendar month, exactly like
/v1/jobs/search. Candidates considered but not returned cost nothing. - At most 25 postings per call and 10 calls a minute per account (
429withRetry-After: 60). - Typical latency is 5 to 15 seconds, up to about 30 when a second planning round is needed. Set client timeouts accordingly.
- Resending a request within 24 hours with the same
Idempotency-Keyheader returns the same page.
Status codes
| Status | Meaning |
|---|---|
200 | Success, including an empty data when nothing matched |
400 | Invalid body |
401 | Missing or invalid API key |
402 | Out of credits |
429 | Per-second plan limit, or this route's 10 calls a minute |
503 | Agentic search is unavailable; use /v1/jobs/search |
Company Page
One company's page — who it is, how its hiring is changing against the market, its technology changes, its corporate group, its business facts, offices and similar companies — via GET /v1/jobs/companies/{key}, with a short summary from POST /v1/jobs/companies/{key}/summary.
Company Lookup
Fetch one company's enriched record with GET /v1/companies/{key}.