curl --fail-with-body --max-time 30 --get "https://api.freshtalent.ai/v1/people" \
--data-urlencode "company=Palantir" \
--data-urlencode "roles=forward-deployed-engineer:18@company" \
--data-urlencode "view=compact" \
--data-urlencode "limit=25" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY"
const params = new URLSearchParams({
company: "Palantir",
roles: "forward-deployed-engineer:18@company",
view: "compact",
limit: "25",
});
const response = await fetch(`https://api.freshtalent.ai/v1/people?${params}`, {
signal: AbortSignal.timeout(30_000), headers: { Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}` },
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const body = await response.json();
console.log(body.data, body.meta.has_more);
import json
import os
from urllib.parse import urlencode
from urllib.request import Request, urlopen
params = urlencode({
"company": "Palantir",
"roles": "forward-deployed-engineer:18@company",
"view": "compact",
"limit": "25",
})
request = Request(
f"https://api.freshtalent.ai/v1/people?{params}",
headers={"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}"},
)
with urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["data"], body["meta"]["has_more"])
{
"data": [
{
"slug": "derek-morrow",
"name": "Derek Morrow",
"headline": "FDE, Anduril",
"linkedin_url": "https://www.linkedin.com/in/derek-morrow",
"current": { "company": "Anduril", "title": "FDE", "months": 14 },
"match": { "company": "Palantir", "months": 50 },
"open_to_work": false,
"between_roles": false,
"exploring": false,
"github_hireable": true
}
],
"query": {
"company": "Palantir",
"roles": "forward-deployed-engineer:18@company"
},
"meta": {
"count": 1,
"limit": 25,
"offset": 0,
"has_more": false,
"total": 1,
"total_capped": false
}
}
Search people
Search the talent graph with structured filters or recruiter-style natural language.
GET
/
people
curl --fail-with-body --max-time 30 --get "https://api.freshtalent.ai/v1/people" \
--data-urlencode "company=Palantir" \
--data-urlencode "roles=forward-deployed-engineer:18@company" \
--data-urlencode "view=compact" \
--data-urlencode "limit=25" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY"
const params = new URLSearchParams({
company: "Palantir",
roles: "forward-deployed-engineer:18@company",
view: "compact",
limit: "25",
});
const response = await fetch(`https://api.freshtalent.ai/v1/people?${params}`, {
signal: AbortSignal.timeout(30_000), headers: { Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}` },
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const body = await response.json();
console.log(body.data, body.meta.has_more);
import json
import os
from urllib.parse import urlencode
from urllib.request import Request, urlopen
params = urlencode({
"company": "Palantir",
"roles": "forward-deployed-engineer:18@company",
"view": "compact",
"limit": "25",
})
request = Request(
f"https://api.freshtalent.ai/v1/people?{params}",
headers={"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}"},
)
with urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["data"], body["meta"]["has_more"])
{
"data": [
{
"slug": "derek-morrow",
"name": "Derek Morrow",
"headline": "FDE, Anduril",
"linkedin_url": "https://www.linkedin.com/in/derek-morrow",
"current": { "company": "Anduril", "title": "FDE", "months": 14 },
"match": { "company": "Palantir", "months": 50 },
"open_to_work": false,
"between_roles": false,
"exploring": false,
"github_hireable": true
}
],
"query": {
"company": "Palantir",
"roles": "forward-deployed-engineer:18@company"
},
"meta": {
"count": 1,
"limit": 25,
"offset": 0,
"has_more": false,
"total": 1,
"total_capped": false
}
}
Search the people visible to the authenticated organization. Use
Example role values:
The people search typeahead endpoint,
Advance
nl for recruiter-style input, the typed filters for reproducible requests, or both. When a field is present in both forms, the explicit query parameter wins. Every response echoes the effective, saveable filter object in query; it does not include pagination or view.
The endpoint requires an API key. See Authentication. Search excludes profiles with data-quality flags unless include_flagged=true is sent.
Natural language and output
string
Optional recruiter-language query, 1-400 characters. The parser recognizes supported availability, role, company, tenure, school, skill, language, and location phrases. Use the structured
certification or certifications parameter for certification matching. Unrecognized words become keyword text rather than a new filter.string
default:"full"
full (default) returns the Person shape. compact returns agent-sized rows with a fixed, smaller shape. See Person response shapes.Company and role filters
string
Case-insensitive fragment matched against a company name or LinkedIn URL. The Gateway requires at least one character and does not impose a further length limit.
integer
Minimum tenure in months, at least
1. With company, matching stints at that company are counted; without it, a person must have a qualifying stint at some company.boolean
true requires a current position at the matching company, or at any company when company is omitted. false does not add a current-position constraint. If combined with company_left_min_months_ago, the left-company condition takes precedence.integer
Requires an integer from
1 to 600. Requires an ended, non-current stint at the matching company, or at any company when company is omitted, that ended at least this many months ago. A current stint at the matching company is excluded.string
A year (
YYYY) or month (YYYY-MM), checked at the first day of that year or month, not any overlap across the whole period. Known position start/end bounds must contain that date; a missing bound is treated as open-ended. It can be used with or without company.string
One role-family value. Kept for single-role clients; prefer
roles when expressing more than one role or a title match.integer
Minimum role tenure, at least
1. Requires role_family or roles. With company, the legacy role_family form counts tenure at the matched company; otherwise it counts career-wide tenure.string
Comma-separated role filters, not a JSON array. Up to 5 parsed filters are used; the request value is at most 1500 characters. A family token is
family or family:months; add @company to scope it to company. For title matching, use t.contains.<encoded-title>.<months> or t.exact.<encoded-title>.<months>. Multiple titles in one title token are separated by ;. Role-family names are at most 64 characters and month values are capped at 600 by the parser. @company falls back to career-wide matching when company is absent. If roles produces usable filters, it takes precedence over role_family. Surviving role filters are combined with AND; multiple titles within one title token are alternatives (OR). Invalid or duplicate tokens can be skipped rather than rejected, so inspect the echoed query and returned matches.roles=forward-deployed-engineer:18@companyroles=software-engineer:24,founding-engineer:12roles=t.contains.Staff%20ML%20Engineer.12roles=t.exact.Staff%20ML%20Engineer;Principal%20Engineer.12@company
Text and education
string
Free-text keyword match. The Gateway requires at least one character. Use
q_in to select the field.string
Where
keywords applies: people (default, full-text over name and headline), name, or headline.string
Case-insensitive school-name fragment. The Gateway requires at least one character.
Skills and languages
string
Single skill term, kept as a compatibility alias. Prefer
skills for multiple terms. The Gateway requires at least one character.string
Semicolon-, comma-, or
or-separated terms. The request value is at most 800 characters; up to 8 distinct terms are parsed and each term is limited to 64 characters. Each term matches a LinkedIn skill, current or past employer, or headline. Terms use OR matching by default, or AND matching with skill_match=all.string
default:"any"
any (OR) or all (AND) for skills. The default is any.string
Single spoken-language term, kept as a compatibility alias. Prefer
languages for multiple terms. The Gateway requires at least one character.string
Semicolon-, comma-, or
or-separated spoken languages. The request value is at most 800 characters; up to 8 distinct terms are parsed and each term is limited to 64 characters. Terms match Language graph nodes only, not employers or headlines. Terms use OR matching by default, or AND matching with language_match=all.string
default:"any"
any (OR) or all (AND) for languages. The default is any.string
Semicolon-, comma-, or
or-separated spoken languages to exclude. The request value is at most 800 characters; up to 8 distinct terms are parsed. This exclusion is independent of languages; a person matching any excluded Language node is dropped.string
Single-term compatibility alias for
certifications. The value is a 1–200 character, case-insensitive literal substring matched against the name or issuing authority of an actual recorded certification edge. This indicates a recorded credential; it does not verify that the certification is current, valid, or unexpired. This filter combines with every other people filter using AND.string
String containing a JSON list of up to 10 distinct terms after trimming and case-insensitive deduplication. Each term is 1–200 characters and is matched as a case-insensitive literal substring of a recorded certification name or issuing authority. The terms use OR matching by default, or AND matching with
certification_match=all. This indicates recorded credentials; it does not verify that a certification is current, valid, or unexpired. The legacy singular certification parameter remains supported.string
default:"any"
any (OR) or all (AND) for certifications. The default is any. This has no effect when no certification terms are supplied.GET /suggest?q=..., also returns certification suggestions. Each suggestion uses the existing certification filter: title is the credential name, key is its normalized name, and count is the number of distinct audience-visible, unflagged people holding that named credential. Renewals and duplicate certification edges count once per person. Suggestions may include authority when the name has one unambiguous issuer.
Location
string
Up to 20 semicolon-separated cities, regions, countries, or continents. Each selection is trimmed to 120 characters. A comma stays inside a place, such as
Paris, France. Included locations are ORed. Continents expand to their configured country groups.string
Up to 20 semicolon-separated locations, with a maximum request length of 2420 characters and 120 characters per selection. Exclusions take priority over included locations. Without
location, the search runs everywhere except these places.Availability and data quality
boolean
Match the profile’s LinkedIn-reported open-to-work flag. Both
true and false are accepted.boolean
Match whether the person has recorded work history and no position marked current. Both
true and false are accepted.boolean
Match FreshTalent’s exploring assessment. It is distinct from open-to-work and between-roles. Both
true and false are accepted.boolean
Match GitHub’s reported
hireable field. Both true and false are accepted.boolean
Include profiles with data-quality flags when
true. The default is false.Pagination
integer
default:"25"
Page size from
1 to 100.integer
default:"0"
Zero-based number of matching rows to skip. Must be
0 or greater.offset by the returned meta.limit while meta.has_more is true. There is no cursor or next URL. meta.total is null for an unfiltered browse and otherwise contains the count calculated for the filter; meta.count is the number returned on this page. has_more currently means the returned page is full, not that another row has been proven to exist. A final extra request may return an empty page. Offset paging is not snapshot-isolated; concurrent graph updates can shift rows.
curl --fail-with-body --max-time 30 --get "https://api.freshtalent.ai/v1/people" \
--data-urlencode "company=Palantir" \
--data-urlencode "roles=forward-deployed-engineer:18@company" \
--data-urlencode "view=compact" \
--data-urlencode "limit=25" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY"
const params = new URLSearchParams({
company: "Palantir",
roles: "forward-deployed-engineer:18@company",
view: "compact",
limit: "25",
});
const response = await fetch(`https://api.freshtalent.ai/v1/people?${params}`, {
signal: AbortSignal.timeout(30_000), headers: { Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}` },
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const body = await response.json();
console.log(body.data, body.meta.has_more);
import json
import os
from urllib.parse import urlencode
from urllib.request import Request, urlopen
params = urlencode({
"company": "Palantir",
"roles": "forward-deployed-engineer:18@company",
"view": "compact",
"limit": "25",
})
request = Request(
f"https://api.freshtalent.ai/v1/people?{params}",
headers={"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}"},
)
with urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["data"], body["meta"]["has_more"])
{
"data": [
{
"slug": "derek-morrow",
"name": "Derek Morrow",
"headline": "FDE, Anduril",
"linkedin_url": "https://www.linkedin.com/in/derek-morrow",
"current": { "company": "Anduril", "title": "FDE", "months": 14 },
"match": { "company": "Palantir", "months": 50 },
"open_to_work": false,
"between_roles": false,
"exploring": false,
"github_hireable": true
}
],
"query": {
"company": "Palantir",
"roles": "forward-deployed-engineer:18@company"
},
"meta": {
"count": 1,
"limit": 25,
"offset": 0,
"has_more": false,
"total": 1,
"total_capped": false
}
}
Errors and recovery
400 invalid_request: fix query validation, a value outside its limit, or the dependency errormin_role_months requires role_familywhen neitherrole_familynorrolesis present.401 unauthorized: provide a valid API key inAuthorization: Bearer ....402 payment_required: restore active organization access.5xxor a transient network failure: retry this read with bounded backoff, jitter, and a timeout.429: slow down and retry with exponential backoff and jitter. The shared Gateway limit is 300 requests per minute per client IP as the Gateway sees it. HonorRetry-After; see Rate limits.
GET can be retried after a transient 429; do not retry a 400 until the request is corrected. See Errors for the common envelope and retry rules.
Persist the echoed
query object with Save query, or reuse its fields on a later people search. Target creation uses a different schema and discovery semantics; see Create target before translating filters. Open a row with Get person when you need the full dossier.