/v1/searches/compileCompile a role brief into a search config
search:readTurn a pasted job description (plus optional context links and a steering prompt) into an editable SearchConfigV2 draft: the levers a search would run. Runs the role extractor and the deterministic lever compiler; no search runs and nothing is billed. Adopt the returned config, edit the levers, then execute. Links (jdUrl, jdLink, contextLinks) are recorded as context and are never fetched, so a link on its own is not enough to compile: send the JD text in jd, or the role in prompt. If briefTruncated comes back true, the extractor only read the first part of the brief.
Request body CompileSearchRequest
jdstring | nullThe job description text, pasted in full. This is the primary signal the compiler reads.
max 50000 charsjdUrlstring | nullA link to the job posting. RECORDED as context and NEVER fetched, so a link on its own cannot compile: paste the text in `jd` or describe the role in `prompt`.
max 2048 charsjdLinkstring | nullAlias for `jdUrl`. Same behaviour: recorded, never fetched.
max 2048 charscontextLinksarray of stringExtra reference URLs. Recorded as context; never fetched.
max 20 itemslinksarray of stringAlias for `contextLinks`.
max 20 itemspromptstring | nullFree-text steering, either instead of a job description or on top of one ("bias toward infra work, ignore the management line").
max 50000 chars
Responses
200The compiled V2 draftconfigobject requiredThe editable draft config. Nothing has been searched and nothing has been charged: review the levers, edit them, then send this to `POST /v1/searches/execute`.
versionnumber requiredMarks this as a version 2 config. The only required field alongside `query`.
2querystring | null requiredFree-text description of who you are looking for, matched semantically. This is the one field that carries the intent; every lever below narrows it.
max 500 charsentitystring default: "auto"Which index to search. Leave `auto` to let the compiler route. Pin one when a lever you need landed in `unsupported`: `User` searches GitHub developers, `Repo` searches repositories and returns their contributors, and `LinkedInUser` searches the LinkedIn overlay, which covers only the minority of developers we hold a match for and therefore reaches a much smaller population.
autoUserRepoLinkedInUserrequiredLanguagesarray of string default: []Programming languages that MUST be present, as a hard filter on repositories. This is the only language axis that hard-filters: `levers.skills` and `levers.technologies` are semantic aims. Set it only for languages you genuinely require, since it removes everyone else.
max 20 itemsearlyCareerSignalsboolean default: falseBias the search toward people early in their career: adds a learning-in-public angle and inverts the devrank weighting, so established high-reputation accounts stop dominating the ranking.
aiFocusedSignalsboolean default: falseBias every angle toward AI, agent, LLM, retrieval and inference work.
includeUnconventionalTitlesboolean default: falseKeep people whose job title is not a conventional engineering one but who ship code anyway. Leave false and the LinkedIn lane restricts to engineering functions.
leversobject default: {}The narrowing levers. Every one is optional and every collection defaults to empty, so a query-only config is a legal search and you set only the axes you actually want to constrain. Check `appliedLevers` and `unsupported` on the response to see which of them actually bound.
skillsarray of string default: []Demonstrated abilities, matched against what the person has actually built. A semantic aim, not a hard filter — use `requiredLanguages` when something is genuinely mandatory.
max 20 itemsinterestsarray of string default: []Topics and problem areas the person works on or gravitates toward. Distinct from `skills`, which is about demonstrated ability; the two are never collapsed. Setting this routes the search toward the repository lane.
max 20 itemstechnologiesarray of stringLanguages, frameworks and systems, drawn from a CLOSED vocabulary — a value outside it is rejected rather than silently treated as free text. This is the concrete tooling axis; `skills` stays the conceptual one.
max 20 itemslocationobjectWhere the person is. Every location signal here is SELF-REPORTED by the developer and never verified.
countriesarray of string default: []Countries, matched against our geocoding of the self-reported location.
max 20 itemscitiesarray of string default: []Cities, matched against our geocoding of the self-reported location.
max 20 itemsstatesarray of string default: []States or regions, matched against our geocoding.
max 20 itemsrawarray of string default: []Raw location strings, matched against what the developer literally typed on GitHub.
max 20 itemsstrictbooleanTrue to hard-gate on location, trading recall for precision. False or unset applies it as a bias. Remember that location is self-reported and frequently stale or blank, so a hard gate drops everyone who left the field empty.
requireLinkedinbooleanReturn only developers we hold a LinkedIn match for. A severe narrowing: only a minority of the indexed GitHub population carries an overlay, so this trades most of the corpus for professional detail.
openToWorkbooleanRestrict to people flagged as open to work on their LinkedIn profile. Requires the LinkedIn overlay, so it implies the same narrowing as `requireLinkedin`.
experienceTierstringA coarse experience band to aim at.
earlyupToSeniorseniorallyoeRangeobjectA years-of-experience band, derived from the LinkedIn overlay, so it only constrains people we hold one for.
mininteger | null requiredMinimum years of experience. Null for no lower bound.
min 0maxinteger | null requiredMaximum years of experience. Null for no upper bound.
min 0
professionalobjectEmployment and education levers. Every one of these comes from the LinkedIn overlay, so setting any of them implicitly restricts the search to the minority of developers we hold a match for.
currentCompaniesarray of string default: []Companies the person works at NOW.
max 20 itemspastCompaniesarray of string default: []Companies the person used to work at.
max 20 itemsmustHaveWorkedAtarray of string default: []Companies the person must have worked at at some point, current or past. The either-or form of the two fields above.
max 20 itemsexcludeCurrentCompaniesarray of string default: []Companies to exclude people currently working at. This is how you avoid sourcing out of your own company or a client’s.
max 20 itemsschoolsarray of string default: []Institutions the person attended.
max 20 itemstitlesarray of string default: []Job titles to aim at.
max 20 itemsindustriesarray of string default: []Industries the person works in.
max 20 itemscompanySizeTierstringThe size band of the person’s current employer.
1-5051-200201-20002000+companiesModestringHow the company lists bind. `exact` matches the named companies only. `similar` expands them to comparable companies, which widens reach at the cost of precision.
exactsimilarsimilarCompaniesarray of string default: []Companies to treat as reference points for "companies like these", rather than as an allowlist in themselves.
max 20 itemsincludePastbooleanMatch against the full employment history rather than the current employer only. Defaults to true.
githubobjectGitHub-side levers: stars, followers, seed repositories, and the devrank floor.
starRangeobject | null default: nullA star band on the person’s repositories. Null, the default, means no bound at all rather than a missing value.
mininteger | null requiredLower bound, inclusive. Null for no lower bound.
min 0maxinteger | null requiredUpper bound, inclusive. Null for no upper bound.
min 0
similarReposarray of string default: []Seed repositories, as `owner/name`. How they bind depends on `repoMode`: expanded to similar repositories, or matched exactly.
max 20 itemscontributedReposarray of string default: []Repositories the person COMMITS to rather than owns, as `owner/name`. This is how you find the people behind a repository they do not own — a company repo, or a large open-source project.
max 20 itemsminDevrankinteger | null default: nullA floor on devrank, the same 0–100 standing returned as `developer.stats.crackedScore`. Measured against the WHOLE indexed GitHub population rather than a peer group, so it is a blunt reputation gate and not a like-for-like comparison within a role. Null, the default, means no floor.
min 0, max 100hideHighProfilebooleanSuppress very high-profile accounts, which otherwise dominate a ranking without being realistic hires.
repoModestring default: "similar"How `similarRepos` binds. `similar`, the default, expands each seed to comparable repositories. `exact` matches only the repositories you named.
similarexactfollowerRangeobject | nullA band on GitHub follower count. Null bounds mean no limit on that side.
mininteger | null requiredLower bound, inclusive. Null for no lower bound.
min 0maxinteger | null requiredUpper bound, inclusive. Null for no upper bound.
min 0
minContributionsLastYearinteger | nullTHE DEAD-ACCOUNT GATE. A floor on GitHub contributions in the trailing 365 days; ANDs with minContributionsLastMonth when both are set. Recruiter Bot ships 100 here, and the absence of this floor is the single largest source of low-signal developers in a result set. This is NOT an index filter — the serving user index carries no activity column — so it is enforced after hydrate against a LIVE contribution-calendar read of the returned window only. Two consequences worth planning for: it narrows the page rather than the corpus (a tight floor can shorten a page, and countStatus reports it), and it is the one lever that costs a GitHub call, so an unset floor costs nothing at all. A developer GitHub did not answer for is KEPT, never dropped — the gate degrades open rather than emptying a page when the upstream is slow.
min 0, max 100000minContributionsLastMonthinteger | nullThe trailing-28-day half of the dead-account gate, ANDed with minContributionsLastYear when both are set and answered by the same single live read. Recruiter Bot ships 20 here. Catches the developer who was active earlier in the year and has since gone quiet, which the yearly floor alone lets through.
min 0, max 100000
filterobjectA raw filter expression, as an escape hatch for constraints the levers do not express. Opaque here and validated by the compiler; prefer the levers.
boostarray of object default: []Signal weights that RE-RANK results. These never filter anyone out: the signals available here have too little coverage to be used as a hard gate, so they only reorder what already matched.
max 20 itemssignalstring requiredThe signal to weight. Read `GET /v1/search/capabilities` for the signals that exist and whether each can be pushed to the index.
max 200 charsweightnumber requiredIts weight. Positive favours the signal, negative penalises it.
rankobjectHow to rank the results. Ranking today is page-local (see `rankScope` on the response), so this reorders the page rather than reaching deeper into the index.
presetstringA named ranking preset.
max 200 charsweightsarray of objectExplicit per-signal weights, as an alternative to a preset.
max 20 itemssignalstring requiredThe signal to weight.
max 200 charsweightnumber requiredIts weight.
summarystring requiredA short plain-language readback of how the brief was interpreted, for a human to sanity-check before spending credits.
briefTruncatedbooleanTrue when the extractor could not read the whole brief and only succeeded on a shortened re-attempt, meaning the tail of your job description did not influence these levers. Absent means the whole brief was read.
401No or invalid credentialcodestring requiredStable machine-readable error code. Branch on this, never on the numeric status.
bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errormessagestring requiredHuman-readable explanation of the refusal.
statusinteger requiredThe HTTP status code, repeated in the body.
remedyobjectA self-serve path forward, when one exists (a 402 points at the credit top-up).
kindstring requiredWhat kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.
topupconnect_mailboxurlstring requiredWhere to go to clear the condition: an API path, or a web app page when only a person can.
403Missing search:readcodestring requiredStable machine-readable error code. Branch on this, never on the numeric status.
bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errormessagestring requiredHuman-readable explanation of the refusal.
statusinteger requiredThe HTTP status code, repeated in the body.
remedyobjectA self-serve path forward, when one exists (a 402 points at the credit top-up).
kindstring requiredWhat kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.
topupconnect_mailboxurlstring requiredWhere to go to clear the condition: an API path, or a web app page when only a person can.