curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/queries" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name":"Palantir FDE leavers",
"query":{
"company":"palantir",
"role_family":"forward-deployed-engineer",
"company_left_min_months_ago":18,
"open_to_work":true
}
}'
const query = {
company: "palantir",
role_family: "forward-deployed-engineer",
company_left_min_months_ago: 18,
open_to_work: true,
};
const response = await fetch("https://api.freshtalent.ai/v1/queries", {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Palantir FDE leavers", query }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const { data: saved } = await response.json();
console.log(saved.id, saved.query);
import json
import os
from urllib.request import Request, urlopen
payload = json.dumps({
"name": "Palantir FDE leavers",
"query": {
"company": "palantir",
"role_family": "forward-deployed-engineer",
"company_left_min_months_ago": 18,
"open_to_work": True,
},
}).encode()
request = Request(
"https://api.freshtalent.ai/v1/queries",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request, timeout=30) as response:
saved = json.load(response)["data"]
print(saved["id"], saved["query"])
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Palantir FDE leavers",
"query": {
"company": "palantir",
"role_family": "forward-deployed-engineer",
"company_left_min_months_ago": 18,
"open_to_work": true
},
"created_at": "2026-09-07T10:00:00.000Z"
}
}
Save query
Persist a compacted echoed people-search object for later reuse.
POST
/
queries
curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/queries" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name":"Palantir FDE leavers",
"query":{
"company":"palantir",
"role_family":"forward-deployed-engineer",
"company_left_min_months_ago":18,
"open_to_work":true
}
}'
const query = {
company: "palantir",
role_family: "forward-deployed-engineer",
company_left_min_months_ago: 18,
open_to_work: true,
};
const response = await fetch("https://api.freshtalent.ai/v1/queries", {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Palantir FDE leavers", query }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const { data: saved } = await response.json();
console.log(saved.id, saved.query);
import json
import os
from urllib.request import Request, urlopen
payload = json.dumps({
"name": "Palantir FDE leavers",
"query": {
"company": "palantir",
"role_family": "forward-deployed-engineer",
"company_left_min_months_ago": 18,
"open_to_work": True,
},
}).encode()
request = Request(
"https://api.freshtalent.ai/v1/queries",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request, timeout=30) as response:
saved = json.load(response)["data"]
print(saved["id"], saved["query"])
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Palantir FDE leavers",
"query": {
"company": "palantir",
"role_family": "forward-deployed-engineer",
"company_left_min_months_ago": 18,
"open_to_work": true
},
"created_at": "2026-09-07T10:00:00.000Z"
}
}
Creates a saved query for the authenticated organization. The recommended source is the
The top-level body accepts only
Those bounds are the
query object returned by GET /people, because it uses the Gateway’s public snake_case filter names. The Gateway compacts the object before storing it: only the allowlisted keys below survive, and null or empty-string values are omitted.
This route creates a new record every time. It does not update an existing query, deduplicate by name, or accept an idempotency key.
string
required
Human-readable name, 1–80 characters. The Gateway trims it before storing it.
null is not accepted.object
required
JSON object. The top-level value must be an object and may be empty. Unknown nested keys are silently dropped by compaction; the endpoint does not validate nested search bounds or require a particular filter.
name and query. Nested query values are compacted according to this allowlist:
| Key | Type | Search-route bounds/defaults and nullability |
|---|---|---|
nl | string | Natural-language search text; GET /people accepts 1–400 chars. Optional; omitted when null/empty. |
company | string | Optional non-empty company filter on GET /people; omitted when null/empty. |
min_company_months | integer | Search minimum 1; optional. |
company_active_during | string | Search accepts 4–7 chars; optional. |
company_current | boolean | Optional; false is preserved. |
company_left_min_months_ago | integer | Search range 1–600; optional. |
role_family | string | Optional non-empty role-family filter. |
min_role_months | integer | Search minimum 1 and requires role_family or roles when searching; optional when saving. |
roles | string | Comma-separated role-token string, not JSON; 1–1,500 chars on GET /people; optional. |
q_in | string enum | people, name, or headline; optional. |
keywords | string | Optional non-empty free-text filter. |
school | string | Optional non-empty school filter. |
skill | string | Optional single-skill filter. |
skills | string | Search accepts 1–800 chars; semicolon/comma-separated terms. Optional. |
skill_match | string enum | any (default search behavior) or all; optional. |
language | string | Optional single-language filter. |
languages | string | Search accepts 1–800 chars; semicolon/comma-separated terms. Optional. |
language_match | string enum | any (default search behavior) or all; optional. |
exclude_languages | string | Search accepts 1–800 chars; semicolon/comma-separated terms. Optional. |
certification | string | Single-term compatibility alias; search accepts 1–200 chars and matches a literal case-insensitive substring of certification name or issuing authority. Optional. |
certifications | string | String containing a JSON list of up to 10 distinct trimmed terms, each 1–200 chars; each term matches a literal case-insensitive substring of certification name or issuing authority. Optional. |
certification_match | string enum | any (default, OR) or all (AND) for certifications; optional. |
location | string | Optional location filter. |
exclude_locations | string | Search accepts 1–2,420 chars; optional. |
open_to_work | boolean | Optional; false is preserved. |
between_roles | boolean | Optional; false is preserved. |
exploring | boolean | Optional; false is preserved. |
github_hireable | boolean | Optional; false is preserved. |
GET /people request bounds, not POST /queries validation. POST /queries only checks that query is an object; it does not validate nested types, enum values, ranges, or dependencies. A value under an allowlisted key is retained if it is not null or "", even if it would not be accepted by GET /people. To ensure a query is executable, copy it from a successful people-search response and use the search route to validate it.
query has no nullable fields in the persisted response: null and empty-string values are removed, while omitted optional keys remain absent. Numeric 0 and boolean false are not removed by compaction, although GET /people would reject values that violate its own bounds.
Response schema
Returns201 with data:
| Field | Type | Nullability and meaning |
|---|---|---|
id | string (UUID) | Never null. |
name | string | Never null after Gateway trimming. |
query | object | Never null; compacted allowlisted object, possibly {}. |
created_at | string (ISO 8601 timestamp) | Never null. |
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Palantir FDE leavers",
"query": {
"company": "palantir",
"role_family": "forward-deployed-engineer",
"company_left_min_months_ago": 18,
"open_to_work": true
},
"created_at": "2026-09-07T10:00:00.000Z"
}
}
curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/queries" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name":"Palantir FDE leavers",
"query":{
"company":"palantir",
"role_family":"forward-deployed-engineer",
"company_left_min_months_ago":18,
"open_to_work":true
}
}'
const query = {
company: "palantir",
role_family: "forward-deployed-engineer",
company_left_min_months_ago: 18,
open_to_work: true,
};
const response = await fetch("https://api.freshtalent.ai/v1/queries", {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Palantir FDE leavers", query }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const { data: saved } = await response.json();
console.log(saved.id, saved.query);
import json
import os
from urllib.request import Request, urlopen
payload = json.dumps({
"name": "Palantir FDE leavers",
"query": {
"company": "palantir",
"role_family": "forward-deployed-engineer",
"company_left_min_months_ago": 18,
"open_to_work": True,
},
}).encode()
request = Request(
"https://api.freshtalent.ai/v1/queries",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request, timeout=30) as response:
saved = json.load(response)["data"]
print(saved["id"], saved["query"])
Errors, side effects, and retry safety
400: missing/invalidnameorquery, a non-objectquery, or a missingX-FreshTalent-Org-IdUUID when using the platform API key. The Gateway does not return a nested-field validation error for unknown query keys; it drops them.401(unauthorized): provide a valid API key.402(payment_required): restore entitlement.429: back off, but first consider whether the insert already succeeded.5xx: the outcome can be unknown. This POST is not idempotent; retrying can create duplicate saved queries. Reconcile with List saved queries and comparenameplus the persisted compactquerybefore deciding whether to retry.
query can be replayed on people search, but is not directly compatible with Target app_query. See Search query compatibility.