---
title: "Add companies to a watchlist"
method: POST
path: "/api/v1/watchlists/{signalId}/companies"
tags: ["Watchlists"]
---

# Add companies to a watchlist

`POST /api/v1/watchlists/{signalId}/companies`

## Request

- Base URL: `https://api.rodz.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.rodz.io/api/v1/watchlists/{signalId}/companies`
- Auth: HTTP bearer

Add companies to a Job Offers Watchlist without recreating the signal — extend the list as your target-account list grows, or replace it when the quarter's territory changes.

**Input:** `signalId` (path), `companies` (array of 1 to 10,000 names, domains or LinkedIn company URLs), and `mode`: `append` (default) or `replace` (companies not in the list stop being checked).

Each entry is either a plain string or an object `{name, domain, linkedinUrl}` (all optional, at least one required) carrying everything you know about the company, e.g. `{"name": "PayFit", "domain": "payfit.com", "linkedinUrl": "https://www.linkedin.com/company/payfit"}`. Strings and objects can be mixed in one call, and entries naming the same company are merged into one watched company carrying all their identifiers.

**Send the LinkedIn company URL whenever you have it.** It identifies the company exactly; a name does not, and two companies sharing a name cannot be told apart from the name alone — the list will look fully matched while watching the wrong one. A name still matches more reliably than a domain, so send the name when the URL is not available. Re-sending a company with its URL added corrects a match made earlier from its name.

**Output:** `added`, `alreadyWatched` (already on the list, details refreshed), `rejected` (neither a usable name nor a domain), `stoppedWatching` (dropped by a `replace`), and `totalWatched`.

A company added here is matched to a real employer on its first check, not immediately — call `GET /companies` afterwards and read `unmatched` to confirm the list is fully covered. Its first check only looks back over the signal's `firstRunLookbackDays`, so adding a company does not dump its whole posting history on you.

## Path parameters

- `signalId` string, required: Id of the watchlist signal, as returned by the create endpoint.

## Request body

- object
  - `companies` union[], required: Companies to watch. Each entry is either a plain string — a name (`"Doctolib"`), a domain (`"doctolib.fr"`) or a LinkedIn company URL — or an object `{name, domain, linkedinUrl}` (all optional, at least one required) carrying everything you know about the company. Both forms can be mixed. Send every identifier you have: the LinkedIn URL identifies the company exactly, and entries naming the same company are merged into one. **Accepted entry forms:** a company name (`"Doctolib"`); a domain or website URL (`"payfit.com"`, `"https://www.payfit.com/fr"`); its LinkedIn company page (`"https://www.linkedin.com/company/payfit"`, in any spelling: with or without `https://` and `www.`, a country subdomain, a trailing path or query; school and showcase pages too); its numeric LinkedIn company id (`"10426145"`) or a Sales Navigator company link (`"https://www.linkedin.com/sales/company/10426145"`), both read as that id; or an object `{name, domain, linkedinUrl}` combining several. Any other LinkedIn link names no company: a person's profile (`/in/`), a post or a group page is refused with 422 `INVALID_COMPANY_ENTRY`, which lists each offending entry, and nothing is saved.
    - union
      - string
      - WatchedCompanyObject: One company, with everything you know about it. The object form of a company-list entry. Send every identifier you have: each one is another way to recognise the company, and together they separate two companies that share a name.
        - `name` string, nullable: The company name as you know it, e.g. `"PayFit"`.
        - `domain` string, nullable: The company website, e.g. `"payfit.com"`. A full URL is accepted and reduced to its domain.
        - `linkedinUrl` string, nullable: The company LinkedIn page, e.g. `"https://www.linkedin.com/company/payfit"`. The most precise identifier of the three. A Sales Navigator company link (`/sales/company/<id>`) or the numeric LinkedIn company id is accepted too, and so are school and showcase pages; any other LinkedIn link (a person's profile, a post) is refused with 422 `INVALID_COMPANY_ENTRY`.
  - `mode` 'append' | 'replace': `append` (default) adds these to the list. `replace` makes this the whole list: companies not in it stop being checked.

## Response `200`

Successful Response

- object: What a list change actually did. Every outcome is reported separately because they mean different things to the caller: `rejected` is input to fix, `alreadyWatched` is a no-op, and `stoppedWatching` is people who dropped off a `replace`.
  - `added` integer, required: Entries now being watched that were not before.
  - `alreadyWatched` integer: Entries already on the list, left unchanged.
  - `rejected` integer: Entries that could not be used — not a valid identifier for this list.
  - `stoppedWatching` integer: Entries dropped because `mode` was `replace`. Always 0 on `append`.
  - `totalWatched` integer, required: Entries being watched after this call.

## Other responses

- `400`: The signal exists but watches the other kind of subject
- `404`: No watchlist signal with this id on your account
- `422`: Validation Error
- `429`: Rate limit exceeded. Retry after the number of seconds in the `Retry-After` response header.

---

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