Natural-language search
Ask in plain English and get a matching list of companies or people — plus a clear list of anything the query could not apply.
Natural-language search
View API reference · Tag Agentic search
We built natural-language search so you can describe who you want in a sentence and get back a list — the same structured company or people search you'd assemble by hand, without filling a filter form.
Try it in the dashboard or call POST /v1/nlp-search/run (slushieRun). Fiber
picks companies or people (never both on one response) and pages with a
single pageToken. You search the same 40M+ companies as structured search.
This is a list search, not a lookup. "Series B SaaS in NYC hiring RevOps" belongs here. "Jane Doe at Acme" or a LinkedIn URL belongs in Kitchen Sink.
What it does
- You send a free-form
query(titles, stages, industries, locations, hiring signals, named employers, schools, languages, Open to Work, and similar). - Fiber turns that sentence into structured search filters and runs the search.
- You get matching companies or people,
parsedParams(how the sentence was read),unsupportedFilters(clauses that could not be applied), and a cursor for the rest of the match set.
Company-shaped questions return companies. Questions about people — including
people at companies that match filters — return people. You do not choose the
result type on slushieRun.
What you can call
| Operation | What it does | Returns |
|---|---|---|
slushieRun | Read a sentence and run the search it describes | A page of companies or people, plus how the sentence was read |
nlpSearchParse | Read a sentence and stop — no search | The structured filters and a suggestedAction, so you can check the interpretation before paying for results |
multiSourceSearch | Run the same kind of sentence across LinkedIn and Maps / the open web | A page of companies or prospects, for local businesses with a thin LinkedIn footprint |
slushieRun is the one you want by default. Reach for nlpSearchParse when
you need to see the interpretation first, and multiSourceSearch when your
targets are storefronts and clinics rather than tech companies.
What you send
slushieRun takes a single English sentence. Everything else is optional.
| Field | Notes |
|---|---|
query | The sentence, up to 5,000 characters. Required on the first request, ignored once you pass a pageToken. |
pageSize | 1–1,000, defaults to 25. Applies to whichever result type comes back. |
pageToken | The nextPageToken from the previous response. Send it alone to get the next page. |
companyExclusionListIDs | Exclusion lists to subtract from company results, or to narrow the company side of a people search. |
peopleExclusionListIDs | Exclusion lists to subtract from people results. |
getDetailedEducation | Fills a detailedEducation array on each person — school URLs, locations. Slows the call down; leave it off unless you need it. People results only. |
getDetailedWorkExperience | Same idea for employers, in detailedWorkExperience. People results only. |
What you get back
| Field | Meaning |
|---|---|
results.resultType | companies or people. Read this first — it tells you which array is populated. |
results.companies / results.people | The matches, in the same shape as structured search. Only one of the two is present. |
parsedParams | The filters your sentence became, and a queryType of structuredSearch, personLookup, companyLookup, or unprocessable. This is how you confirm we read you correctly. |
unsupportedFilters | Clauses we could not turn into a filter. They were dropped, not guessed. |
autoLoosenings | Constraints we relaxed because the exact combination matched nobody. First page only — null on paginated requests. |
notices | Plain-English warnings, such as an empty result set or the end of pagination. null when there is nothing to say. |
nextPageToken | Pass it back for the next page. Absent when you have everything. |
searchId | Stable across every page of one search — useful for logging and support. |
chargeInfo.creditsCharged | What this call actually cost. Authoritative. |
nlpSearchParse returns parsedParams, unsupportedFilters, searchId, and
a suggestedAction of companySearch, profileSearch, combinedSearch,
personLookup, companyLookup, or none — your next step in one field.
const response: Response = await fetch("https://api.fiber.ai/v1/nlp-search/run", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
apiKey: process.env.FIBER_API_KEY,
query: "YC W24 companies",
pageSize: 25,
}),
});
const payload: unknown = await response.json();multiSourceSearch uses a two-step body: { request: "initial", query, pageSize }
first, then { request: "subsequent", cursor } for each page. pageSize is
locked for the session; a null nextCursor means you're done. For Maps-only
local search, see Google Maps.
Rate limit on these routes: 120 requests per minute. Plan for a 60-second
timeout on slushieRun — interpretation plus search can take longer than a
typical lookup.
What you can ask for
These are some of the filters you can mix in one sentence — not an exhaustive list. If a clause can be a structured company or people filter, we apply it.
Companies
| Ask for | Example |
|---|---|
| Accelerator and batch | "YC W24 companies" |
| Investor | "Sequoia-backed Series B companies in the Bay Area" |
| Stage, industry, size | "Series B SaaS companies with 50-200 employees" |
| Funding amount or recency | "AI companies that raised over $20M in the last 6 months" |
| Founded date | "SaaS companies founded after 2020" |
| Revenue | "bootstrapped SaaS companies with $5M-$50M revenue" |
| Tech stack | "companies using Kubernetes and Snowflake" |
| Open roles | "Series B SaaS companies hiring RevOps" |
| HQ location | "fintech companies based in NYC" |
People
| Ask for | Example |
|---|---|
| Title and seniority | "VPs of Sales at fintech companies in NYC" |
| Title exclusions | "VPs of Sales …, not interns" |
| Named employer — current, past, or ever | "people at Stripe", "ex-Google engineers", "people who have worked at Google" |
| People at companies that match company filters | "engineers at Series B AI startups" |
| Open to Work | "product managers open to work in NYC" |
| School, major, years of experience | "Stanford CS grads, 4+ years, now senior engineers in the Bay Area" |
| Skills on the profile | "engineers skilled in Go and Kafka" |
| Languages | "people who speak Spanish at companies in Spain" |
| Recently joined / new in role | "engineers who recently joined Series B startups" |
| Named-company exclusion (including never) | "never worked at McKinsey" |
Named past employer plus current company filters in the same sentence is
supported — e.g. "ex-PMs at Google who are now founders at startups".
Examples
| Intent | Example |
|---|---|
| Accelerator + batch | "YC W24 companies" |
| Named alumni now at a kind of company | "ex-FAANG engineers now at startups" |
| Named past employer + current employer set | "Stripe alumni who now work at YC companies" |
| Stage, industry, size, hiring | "Series B SaaS companies in NYC with 50-200 employees hiring engineers" |
| Open to Work | "product managers open to work in NYC" |
| Language + market | "people who speak Spanish at companies in Spain" |
| School + seniority + location | "Stanford CS grads, 4+ years, now senior engineers in the Bay Area" |
| Investor + stage + HQ | "Sequoia-backed Series B AI companies in the Bay Area" |
What is not supported
Anything we cannot turn into a filter is dropped, not guessed — and
sometimes a nearby filter is applied instead. Always read unsupportedFilters
and parsedParams before you trust the list.
| You asked for | What happens |
|---|---|
| Past employers described by company attributes (not a named company) — e.g. "alumni of Sequoia-backed companies", "people who used to work at Series A healthcare startups" | Not a past-experience filter. Those clauses show up in unsupportedFilters. |
| "ex-YC founders" and similar (past role at an accelerator/investor set, not a named company) | Often interpreted as founders at YC companies now, with an empty unsupportedFilters. If you mean alumni of YC companies, say that and check parsedParams. |
| People by department / job function — e.g. "engineering department", "marketing team" | There is no department field. We match job titles or profile keywords instead (e.g. "Software Engineer", "Marketing Manager"). |
| Remote / hybrid / on-site / willing to relocate as a person preference | Not available on profiles. (Remote job postings on a company search are a different ask.) LinkedIn Open to Work is supported when you ask for it. |
| Subjective or intent language — e.g. "interested in AI", "looking to switch vendors", "high performers", "growth mindset" | Dropped. We cannot filter on motives or performance. |
| Salary or compensation — e.g. "earning $200k+" | Not a people/company filter. The clause is listed in unsupportedFilters; the rest of the query still runs. |
| Years of a specific skill — e.g. "3–5 years of Rust" | Only total years of experience is a filter. Name the skill separately. |
| A specific person or company — name + employer, email, domain, or LinkedIn URL | Use Kitchen Sink. slushieRun may route these to a lookup (queryType: personLookup / companyLookup, at most 50 people per request). Kitchen Sink is the dedicated path. |
| A full job description paste (long JD with Summary / Responsibilities / Requirements) | This product expects a short targeting sentence. Pull title, skills, location, and years yourself, then search. |
| Greetings, posts, or anything that is not a company/people list | parsedParams.queryType is unprocessable / suggestedAction is none. |
A 200 with a list is not proof every clause was applied. Empty
unsupportedFilters is not proof the interpretation matched your intent —
check parsedParams.
On the first page only, if the original combination matches nobody,
autoLoosenings may list constraints that were relaxed so you still get a
page. Treat that as a different query than the one you typed.
Using it effectively
Parse before you run, then count before you page. The cheapest way to get a
good list is a three-step loop that costs almost nothing to iterate. Call
nlpSearchParse on your sentence and read parsedParams — 2 credits, no
results. Take the companySearchParams or profileSearchParams it hands back
and pass them straight into the structured count endpoints
(companyCount /
peopleSearchCount),
which are free, to see how big the match set is. Only then call
slushieRun, where you start paying per result. Rewriting a bad sentence at
the parse step costs 2 credits; discovering the problem after pulling 500
profiles costs 500. See counting before you
spend.
Name companies when you mean those companies. The strongest filters we can build from a sentence are the named ones. "Used to work at Stripe" becomes a past-employer filter and works exactly as you'd expect. "Alumni of Sequoia-backed companies" describes a set of employers by attribute, and there is no filter for that — it gets dropped. If you want that cohort, run it as two searches: find the companies first, then search people at them.
Say current, past, or ever — the default is not what you think. "At Google" reads as current employment. "Ex-Google" reads as past. "Has worked at Google" covers both. These three produce genuinely different lists, and the sentence is the only place you get to say which one you meant.
Read parsedParams on the first run of any new query shape. A 200 with 25
results looks like success whether or not we understood you. Once you have
confirmed the interpretation for a query shape, you can trust it for the
variants you send afterwards.
Pull pages with the token, not the query. Send pageToken on its own —
query is ignored once a token is present, and interpretation is not charged
again. Re-sending the sentence starts a fresh search and pays for
interpretation a second time.
Iterate the sentence rather than fighting it. Every run is a fresh
structured search, so tightening a query is a matter of restating it. If
autoLoosenings came back populated, your original combination matched nobody
and you are looking at a broader list than you asked for — tighten one
constraint and run again rather than paging deeper.
One known person or company is not a search. A name plus employer, an
email, a domain, or a LinkedIn URL belongs in
Kitchen Sink. slushieRun can route those to a
lookup, but you pay interpretation for the privilege.
Use cases
Build a sourcing pipeline from a role description. Say the role in one
sentence — "Stanford or MIT CS grads, 4+ years, now senior backend engineers in the Bay Area" — and run slushieRun. Page with nextPageToken until it is
absent, then take the LinkedIn URLs into
Contact Reveal for email and phone. Add
"open to work" to the sentence when you want the warm end of that list
first.
Find accounts at a stage, then the people inside them. This is two
searches, and the first one is the cheap one. Ask for the companies —
"Series B SaaS companies in NYC with 50-200 employees hiring RevOps" — and
you get a company list. Then ask for the people with the same company
constraints in the sentence: "RevOps leaders at Series B SaaS companies in NYC". Feed your CRM's existing accounts in as companyExclusionListIDs so you
are only paying for companies you don't already have.
Track a cohort over time instead of re-searching it. Run the sentence once to get the list, then hand those entities to Tracker and let it tell you when someone changes jobs or a company raises. A search answers "who matches today"; a tracker answers "who changed".
Screen an investor's or accelerator's portfolio. "YC W24 companies" or
"Sequoia-backed Series B AI companies in the Bay Area" returns the batch as a
list. Run nlpSearchParse first and pass the params to companyCount to see
how big it is before you decide the page size.
Give an LLM agent a search tool it can actually use. slushieRun takes one
string and returns structured results, which makes it a clean tool definition
for Claude, Cursor, or your own agent — over REST or MCP. The
agent writes the sentence; it does not have to learn a filter schema. Have it
read unsupportedFilters and parsedParams back before it trusts the list,
and notices to know when to stop paging.
Credits
| Operation | Cost |
|---|---|
slushieRun, first page | 2 credits for interpretation, plus 1 credit per company or person returned |
slushieRun, later pages | 1 credit per result. Interpretation is not charged again. |
nlpSearchParse | 2 credits per request, flat. No result charges — it returns no results. |
multiSourceSearch | 2 credits per company or prospect found. Charged after the page completes. |
companyCount, peopleSearchCount | Free |
Two things worth planning around. Interpretation is charged once per search,
not once per page, so a 500-result pull costs 2 credits more than the results
themselves — paging is the cheap part and re-sending the sentence is the
expensive mistake. And multiSourceSearch is twice the per-result price of
slushieRun, which is the trade for reaching businesses that have no real
LinkedIn presence; use it when LinkedIn genuinely won't answer, not by default.
A first page that returns nothing still cost you interpretation.
chargeInfo.creditsCharged on the response is authoritative. See
Billing & credits.
Natural-language search is also available in the Fiber dashboard and through MCP.