curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/people" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"urls":["https://www.linkedin.com/in/derek-morrow"]}'
const id = "550e8400-e29b-41d4-a716-446655440000";
const response = await fetch(`https://api.freshtalent.ai/v1/targets/${id}/people`, {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ urls: ["https://www.linkedin.com/in/derek-morrow"] }),
});
const body = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${JSON.stringify(body)}`);
console.log(body.data.added, body.data.target.watching_count);
import json
import os
import urllib.request
payload = json.dumps({"urls": ["https://www.linkedin.com/in/derek-morrow"]}).encode()
request = urllib.request.Request(
"https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/people",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["data"]["added"], body["data"]["target"]["watching_count"])
{
"data": {
"added": 2,
"already_added": 1,
"target": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"kind": "manual",
"name": "Q4 backend pipeline",
"linkedin_url": null,
"auto_admit": false,
"watching_count": 216,
"proposed_count": 0,
"excluded_count": 0,
"current_count": 0,
"alum_count": 0,
"enriched_count": 191,
"member_count": 216,
"allocation_used": 155,
"in_graph_count": 61,
"result_limit": null,
"created_at": "2026-10-26T11:43:00.000Z",
"query": { "input": "Q4 backend pipeline" },
"job": {
"id": "7b1d9b3c-0b8f-4d4d-a8b4-72ef8fbb4c81",
"status": "pending",
"phase": "queued",
"total": 0,
"completed": 0,
"failed": 0,
"error": null
}
}
}
}
Add target people
Add people to a manual Target by LinkedIn profile URL. They start as watching.
POST
/
targets
/
{id}
/
people
curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/people" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"urls":["https://www.linkedin.com/in/derek-morrow"]}'
const id = "550e8400-e29b-41d4-a716-446655440000";
const response = await fetch(`https://api.freshtalent.ai/v1/targets/${id}/people`, {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ urls: ["https://www.linkedin.com/in/derek-morrow"] }),
});
const body = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${JSON.stringify(body)}`);
console.log(body.data.added, body.data.target.watching_count);
import json
import os
import urllib.request
payload = json.dumps({"urls": ["https://www.linkedin.com/in/derek-morrow"]}).encode()
request = urllib.request.Request(
"https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/people",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["data"]["added"], body["data"]["target"]["watching_count"])
{
"data": {
"added": 2,
"already_added": 1,
"target": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"kind": "manual",
"name": "Q4 backend pipeline",
"linkedin_url": null,
"auto_admit": false,
"watching_count": 216,
"proposed_count": 0,
"excluded_count": 0,
"current_count": 0,
"alum_count": 0,
"enriched_count": 191,
"member_count": 216,
"allocation_used": 155,
"in_graph_count": 61,
"result_limit": null,
"created_at": "2026-10-26T11:43:00.000Z",
"query": { "input": "Q4 backend pipeline" },
"job": {
"id": "7b1d9b3c-0b8f-4d4d-a8b4-72ef8fbb4c81",
"status": "pending",
"phase": "queued",
"total": 0,
"completed": 0,
"failed": 0,
"error": null
}
}
}
}
Adds people to a Target created with
kind: "manual". URLs are normalized and de-duplicated the same way as on Create target. New people start as watching; a person who was excluded comes back as watching; people already watching are left alone.
Request
string
required
Manual Target UUID.
string[]
required
1–2000 strings, each at most 500 characters. Values that are not LinkedIn profile URLs are ignored; at least one must be valid.
Response
202 Accepted returns { "data": { "added": integer, "already_added": integer, "target": Target } }. added counts people who became watching (new or restored); already_added counts people who were already watching. target uses the schema from List targets.
People already in the graph are ready at once. Everyone else is loaded in the background. If a load job is already queued or running for this Target, the new people join it or get a follow-up load when it finishes; no second job is queued.
{
"data": {
"added": 2,
"already_added": 1,
"target": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"kind": "manual",
"name": "Q4 backend pipeline",
"linkedin_url": null,
"auto_admit": false,
"watching_count": 216,
"proposed_count": 0,
"excluded_count": 0,
"current_count": 0,
"alum_count": 0,
"enriched_count": 191,
"member_count": 216,
"allocation_used": 155,
"in_graph_count": 61,
"result_limit": null,
"created_at": "2026-10-26T11:43:00.000Z",
"query": { "input": "Q4 backend pipeline" },
"job": {
"id": "7b1d9b3c-0b8f-4d4d-a8b4-72ef8fbb4c81",
"status": "pending",
"phase": "queued",
"total": 0,
"completed": 0,
"failed": 0,
"error": null
}
}
}
}
Examples
curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/people" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"urls":["https://www.linkedin.com/in/derek-morrow"]}'
const id = "550e8400-e29b-41d4-a716-446655440000";
const response = await fetch(`https://api.freshtalent.ai/v1/targets/${id}/people`, {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ urls: ["https://www.linkedin.com/in/derek-morrow"] }),
});
const body = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${JSON.stringify(body)}`);
console.log(body.data.added, body.data.target.watching_count);
import json
import os
import urllib.request
payload = json.dumps({"urls": ["https://www.linkedin.com/in/derek-morrow"]}).encode()
request = urllib.request.Request(
"https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/people",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["data"]["added"], body["data"]["target"]["watching_count"])
Lifecycle and retry safety
Adding is idempotent for the same URLs: a replay reports them asalready_added and changes nothing. To take someone off the Target, set them to excluded with Accept target members.
Errors and recovery
400 invalid_request: missingurls, more than 2000 entries, no valid profile URL, or the Target is notkind: "manual". Fix the request.401 unauthorized,402 payment_required,403 upgrade_required: fix the API key or organization access.404 not_found: the Target is not in the authenticated organization.409 conflict: a message startingslot_limit:means the new people need more monitoring slots than remain. Nothing was added. Check with Preview target people, remove some URLs, or add capacity.429/5xx: back off. Replaying is safe.
Next
- List target members with
depth=stubto see who is still loading. - Get target to follow
enriched_countand the job.