Scoring
How to check how well one person, or a whole list, fits a customer profile.
This page shows how to score people against a customer profile, one at a time or a whole list, and how to read the results.
A score says how well a person fits. It has a fit from 0 to 100, a label and one sentence of reason. How a score is worked out is never shown.
The score
{
"id": "scr_5TqP2mWd",
"object": "score",
"person": { "id": "per_9aQw1XyZ", "object": "person", "name": "Ana Lima", "headline": "CFO at Lumen Pay", "company": "Lumen Pay", "location": "London, United Kingdom", "linkedin_url": "https://www.linkedin.com/in/ana-lima" },
"customer_profile": "cp_6TnR4cVd",
"scoring_run": null,
"fit": 86,
"label": "strong_fit",
"reason": "CFO at a 180-person UK fintech, the kind of buyer this profile aims for.",
"status": "done",
"excluded_because": null,
"problem": null,
"scored_at": "2026-10-23T09:14:02Z"
}| Field | What it holds |
|---|---|
fit | From 0 to 100. null unless status is done. |
label | strong_fit (70 to 100), good_fit (60 to 69), maybe (50 to 59) or weak_fit (0 to 49). These are the bands the app's scout screens show: Strong fit, Good fit, Worth a look, and no label for a weak fit. |
reason | One sentence on why the person fits or does not. |
status | done (has a score), excluded (left out and not scored: by the profile's rules, the do-not-contact list or the already-scored skip) or failed (done. could not read or score the person). |
excluded_because | For an excluded person: outside_territory, not_a_buyer, do_not_contact or already_scored. |
problem | For a failed score, what went wrong in plain words. |
scoring_run | The run that scored the person. null for a single score. |
When status is excluded or failed, there is no score: fit, label and reason are null.
The main calls
Score one person
POST /scores scores one person and answers in the same call. Send the person's LinkedIn link, and a customer_profile or leave it out to use the workspace's default profile.
curl https://api.done.com/v1/scores \
-H "Authorization: Bearer $DONE_API_KEY" \
-H "Idempotency-Key: 6f1d2c0e-4b8a-4c1e-9d55-2a7c1f0b9e31" \
-H "Content-Type: application/json" \
-d '{
"linkedin_url": "https://www.linkedin.com/in/ana-lima",
"customer_profile": "cp_6TnR4cVd"
}'Preview a run
POST /scoring_runs/preview takes the same body as a run, starts nothing and charges nothing. It says how many people you sent, how many would be left out because they were scored in the last 90 days, how many would be scored, and the most the run would cost. It is a read, so it needs no Idempotency-Key.
curl https://api.done.com/v1/scoring_runs/preview \
-H "Authorization: Bearer $DONE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_profile": "cp_6TnR4cVd",
"links": ["https://www.linkedin.com/in/ana-lima", "https://www.linkedin.com/in/tomas-reyes"]
}'{
"object": "scoring_run_preview",
"customer_profile": "cp_6TnR4cVd",
"ready": true,
"sent": 120,
"skipped_already_scored": 20,
"to_score": 100,
"estimated_credits": 100
}ready is false when the profile is not ready for scoring. For a Sales Navigator link, sent and skipped_already_scored are null, because the people are only found as the pages are read.
Score a list
POST /scoring_runs starts a run that scores many people against one customer profile. Send either:
links: 2 to 500 different LinkedIn profile links, orsales_navigator_urlwithpages(1 to 100): done. reads that Sales Navigator people search and scores the people it finds. Each page is about 25 people.
Not both.
curl https://api.done.com/v1/scoring_runs \
-H "Authorization: Bearer $DONE_API_KEY" \
-H "Idempotency-Key: 0b7d3a52-9c1f-4e0a-8a6d-5f2e91c4b7aa" \
-H "Content-Type: application/json" \
-d '{
"customer_profile": "cp_6TnR4cVd",
"links": ["https://www.linkedin.com/in/ana-lima", "https://www.linkedin.com/in/tomas-reyes"],
"skip_already_scored": true,
"territory": "person",
"name": "UK finance leaders October"
}'It answers 202 at once with the run. The work goes on in the background. Poll GET /scoring_runs/{id} every 10 to 30 seconds until status is done, failed or cancelled (a paused run resumes by itself, so keep polling), or listen for the scoring_run.completed event.
| Run status | Meaning |
|---|---|
pending | Queued. |
running | Scoring. |
waiting | Waiting on something outside the run, such as a search page. |
paused | Stopped by done. It resumes by itself. |
done, failed, cancelled | End states. |
The run shows its progress in people (sent, skipped_already_scored, to_score, done, excluded, failed) and in credits (held and spent).
Read the results
GET /scoring_runs/{id}/results lists the scores a run has made so far, best fit first. People who were left out or could not be scored come after everyone with a score. You can read results while the run is still going.
curl "https://api.done.com/v1/scoring_runs/run_3WmK9pQa/results?min_fit=70&status=done&limit=50" \
-H "Authorization: Bearer $DONE_API_KEY"Filter by status (done, excluded, failed, separated by commas) or by min_fit to keep only people at or above a score. See Lists and pages to read every page.
Cancel a run
POST /scoring_runs/{id}/cancel stops a run that has not finished. People already scored keep their scores and stay charged. Credits set aside for the rest come back. Cancelling cannot be undone. A run that has already ended is refused with run_finished.
GET /scoring_runs lists runs, newest first, and can filter by status and customer_profile.
What it costs
| Call | Cost |
|---|---|
POST /scores | 1, the same as one person in a scoring run. A person on the do-not-contact list is checked, so it costs the same. A person we could not read or score costs nothing. |
POST /scoring_runs | 1 per person scored, plus about 1.4 per Sales Navigator page |
| Preview, get, list, results, cancel | free |
Credits for all the people in a run are set aside when it starts. People left out by the profile's rules, and unused credits, are given back when the run ends. See Credits and costs.
Rules that apply
- People scored in the last 90 days are skipped. People scored for the same customer profile in the last 90 days are skipped and not charged, unless
skip_already_scoredisfalse. Set it tofalseonly to score them again, and every re-score is charged. If everyone was scored recently, the run is refused withall_already_scored. - The profile must be ready. Otherwise the call is refused with
profile_not_ready. A profile that is being researched is refused withresearch_in_progress. - Territory.
territorydecides which place counts for the profile's places:person(where the person is, the default),company_hq(where their company is based) oreither. - Credits. If credits, the plan's capacity or the month's search allowance cannot cover it, the call is refused with
insufficient_creditsorsearch_allowance_reached. - Retries. Send an
Idempotency-Key. A retry with the same key returns the first score instead of scoring and charging again. See Safe retries. - Permissions. Scoring needs
spend. Reading runs and results needsread. Cancelling needsfull.