Competitor Tracker & Co. Docs

Labels

Organize competitors in Competitor Tracker & Co. with labels: create, list, update and delete organization labels, then pin or unpin them on any competitor.

Labels are an organizational primitive on top of competitor subscriptions. The usual case is sorting a mixed roster — direct competitors alongside adjacent or unrelated tools — so they can be segmented for reporting or routing. This page is the human-friendly tour from our lead detective C. T. Lucky. For parameter-level detail, see the API reference in the sidebar.

Shape

A label belongs to one organization and carries:

  • id — opaque, server-minted.
  • name — 1–64 characters: letters, numbers, dash, underscore, spaces. Unique within an organization, case-insensitive (so HRIS and hris collide). Display capitalization is preserved as you typed it.
  • color — one of the preset palette slugs: gray, red, orange, yellow, green, blue, purple, pink. The mapping from slug to hex is the UI's concern.

A competitor can carry zero, one or many labels. Soft caps:

  • 150 labels per organization (422 LabelAccountCapReached on the 151st create).
  • 10 labels per competitor (422 LabelCompetitorCapReached on the 11th assign).

Create

POST /v1/labels

curl -X POST "$CT_API/labels" \
  -H "Authorization: Bearer $CT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "direct", "color": "blue" }'

Returns 201 Created. 409 LabelNameTaken if another active label in your organization already uses that name (case-insensitive). 422 LabelInvalidName and 422 LabelInvalidColor for shape violations the regex or enum reject.

List

GET /v1/labels

Cursor-paginated.

Query parameters:

  • limit — 1–200, default 50.
  • cursor — opaque cursor returned in nextCursor from a prior call.
  • search — free-text match against the label name. Case-insensitive; matches anywhere in the name.
curl "$CT_API/labels?limit=20" \
  -H "Authorization: Bearer $CT_TOKEN"

Hunting for one label in a long list? Pass search:

curl "$CT_API/labels?search=watch" \
  -H "Authorization: Bearer $CT_TOKEN"
{
  "items": [
    {
      "id": "01HF...",
      "name": "direct",
      "color": "blue",
      "createdAt": "2026-05-09T10:00:00.000Z",
      "updatedAt": "2026-05-09T10:00:00.000Z"
    }
  ],
  "nextCursor": null
}

Get one

GET /v1/labels/{id}

Returns the label or 404 LabelNotFound.

Update

PATCH /v1/labels/{id}

Rename or recolor. Both fields are optional but at least one must be present.

curl -X PATCH "$CT_API/labels/01HF..." \
  -H "Authorization: Bearer $CT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Direct" }'

A rename propagates immediately to every competitor the label is attached to. Labels are entities, not strings. 409 LabelNameTaken on a clash with another active label.

Delete

DELETE /v1/labels/{id}

Deletes the label and unpins it from every competitor it was attached to, in the same operation. Returns 204 No Content. The name becomes free for reuse on the next create.

Pin to a competitor

POST /v1/competitors/{id}/labels

curl -X POST "$CT_API/competitors/01HC.../labels" \
  -H "Authorization: Bearer $CT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "labelId": "01HF..." }'

Idempotent. Re-pinning a label that's already attached is a no-op success and doesn't count toward the 10-per-competitor cap. Returns 200 OK with the post-pin label set for the competitor:

{
  "competitorId": "01HC...",
  "labels": [
    { "id": "01HF...", "name": "Direct", "color": "blue", "createdAt": "...", "updatedAt": "..." }
  ]
}

Unpin

DELETE /v1/competitors/{id}/labels/{labelId}

Idempotent. Succeeds with 204 No Content whether the label was attached or not. Use it freely as a "make sure this label is off" call.

Where labels show up

Once attached, labels ride along on the existing read responses so you don't need a second round trip:

  • GET /v1/competitors and GET /v1/competitors/{id}labels: [{ id, name, color }] per competitor in the response.
  • GET /v1/snapshots/{id}labels: [...] for the parent competitor.
  • GET /v1/pages/{trackedPageId}/snapshotslabels: [...] once at the page top, since every snapshot in the list shares the same parent competitor.

Dropping a competitor leaves its label assignments dangling. Re-opening a case on the same URL rehydrates them. If you want a clean slate, unpin first.

On this page