Agentic Search

Search job postings from a plain-language request with POST /v1/jobs/agentic-search.

POST https://api.jobspipe.dev/v1/jobs/agentic-search

Takes 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

  1. 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?".
  2. 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".
  3. 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?" with want: "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.
  4. 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);
}
FieldTypeDescription
querystringRequired. 2 to 500 characters of plain language.
filtersobjectOptional 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.
limitintegerPostings 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_remaining of credits_allowance), and agentic, the plan and how it ran. total_results and next_cursor are always null: there are no further pages.
  • data: the postings, highest relevance first. 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, including injection and, for the top ten, recheck) and lanes (which planned searches returned it).
200 OK
{
  "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 (429 with Retry-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-Key header returns the same page.

Status codes

StatusMeaning
200Success, including an empty data when nothing matched
400Invalid body
401Missing or invalid API key
402Out of credits
429Per-second plan limit, or this route's 10 calls a minute
503Agentic search is unavailable; use /v1/jobs/search

On this page