---
title: "Start Web Run"
method: POST
path: "/api/v1/scout/runs"
tags: ["scout"]
---

# Start Web Run

`POST /api/v1/scout/runs`

## Request

- Base URL: `https://api.nudgelink.co` (the document declares no server; this is the origin it was published from)
- URL: `https://api.nudgelink.co/api/v1/scout/runs`
- Auth: HTTP bearer

## Request body

- ScoutRunRequest
  - `target_limit` integer
  - `score_threshold` integer
  - `extra_keywords` string[], nullable
  - `deep_search` boolean
  - `filters` SearchFilters: Structured LinkedIn people-search filters (Discovery v2, basket 1). Every facet field carries LinkedIn *IDs* (not free text), resolved client-side via the typeahead endpoint (``GET /api/v1/leads/discover/search-parameters``), which proxies Unipile's ``/linkedin/search/parameters``. Sending the wrong shape (free text where an ID is expected) is exactly what made classic search return nothing before, so the model only carries IDs and the adapter drops anything it can't map. Form-supplied filters take priority over the ICP (the ICP only fills a facet the form left blank — see ``lead_finder._assemble_structured_filters``). This model is search-time facets only. Free-text "work keywords" + AND/OR and the company-size buckets are post-fetch SOFT signals and live as separate ``DiscoverRequest`` fields, not here (ТЗ v2 §B6/§B4). ``network_distance`` is deliberately absent: discovery is pinned to 2nd + 3rd degree (1st-degree is excluded — those are usually already in conversation — while 3rd-degree strangers are the target of cold outreach). ``to_filter_dict`` injects ``[2, 3]`` unconditionally so the rule is enforced server-side regardless of the client. Safe even if LinkedIn rejects degree 3 on a free seat: classic search then degrades to keywords-only (see ``unipile.search_profiles``). Confirm free-seat acceptance with ``backend/scripts/discovery_v2_spike.py`` (§E.2).
    - `location` string[]
    - `company` string[]
    - `past_company` string[]
    - `school` string[]
    - `industry` string[]
    - `skills` string[]
  - `work_keywords` string[]
  - `work_keywords_mode` 'and' | 'or'
  - `company_sizes` string[]
  - `filter_labels` object
  - `campaign_id` string, uuid, nullable
  - `brief` ScoutBrief: None = keep the campaign's value; a list (even empty) replaces it. ``mode``: titles (a role in a market), companies (those roles at the ``target_companies`` listed), lookalike (companies like ``seed_companies``, then the roles at the companies found).
    - `mode` 'titles' | 'companies' | 'lookalike', nullable
    - `target_companies` string[], nullable
    - `seed_companies` string[], nullable
    - `titles` string[], nullable
    - `industries` string[], nullable
    - `locations` string[], nullable
    - `exclude_titles` string[], nullable
    - `exclude_companies` string[], nullable
  - `find_more` boolean

## Response `200`

Successful Response

- StartWebRunResponse
  - `run_id` string, uuid, required
  - `reserved_credits` integer, required
  - `status` string, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/nudgelink/apis/nudgelink-api.md) · [All operations](https://skmtc.dev/nudgelink/apis/nudgelink-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nudgelink/apis/nudgelink-api/revisions/baa46e6db986?raw)
