# Competitors

Source: https://ceyo.ai/docs/signal/competitors

### Competitors

Manage tracked and suggested competitors for a project or location, including merges, bulk dismissal, and brand claims.

**Scope:** Project

### List competitors

`GET /projects/{project_id}/competitors?state=tracked`

Returns tracked competitors for the selected project, together with the primary brand and pagination.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `state` | tracked \| suggested | Optional; Default: tracked | Return competitors in one lifecycle state. |
| `q` | string | Optional | Case-insensitive search across name, domain, and aliases. |
| `domain` | all \| with-domain \| without-domain | Optional; Default: all | Return all competitors or filter by whether a domain is present. |
| `sort` | name-asc \| name-desc \| newest \| visibility \| mentions | Optional; Default: name-asc | Sort by name, creation time, visibility rate, or mention count. |
| `page` | integer | Optional; Default: 1 | The 1-based page number. |
| `per_page` | integer | Optional; Default: 25 | Number of competitors per page. Minimum: 1. Maximum: 50; this matches the maximum suggested dataset size. |

#### List response envelope

`project_id`:**uuid**`primary`:**Competitor**`competitors`:**Competitor\[\]**`pagination`:**Pagination**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `primary` | Competitor |  | Primary tracked brand for comparison. |
| `competitors` | Competitor\[\] |  | Competitors matching the filters. |
| `pagination` | Pagination |  | Pagination metadata for competitors. |

#### Competitor

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique competitor identifier. |
| `kind` | primary \| competitor |  | Entity role. Items in competitors are competitor; primary describes the tracked brand. |
| `name` | string |  | Display name used in prompts, results, and reports. |
| `domain` | string \| null |  | Normalized hostname without a scheme, path, query, leading www, or trailing dot. |
| `aliases` | string\[\] |  | Additional names recognized as this entity. Maximum: 10. |
| `status` | active \| inactive |  | Processing status. Competitors are active exactly when tracked and inactive when suggested or dismissed. |
| `competitor_state` | tracked \| suggested \| dismissed \| null |  | Competitor management lifecycle state. Null for the primary brand. |
| `created_at` | datetime |  | Competitor creation time. |
| `updated_at` | datetime |  | Last competitor update time. |

#### Pagination

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `page` | integer |  | Current 1-based page number. |
| `per_page` | integer |  | Requested page size. Minimum: 1. Maximum: 50. |
| `total` | integer |  | Total competitors matching the applied filters. |
| `total_pages` | integer |  | Total pages at the current per\_page value. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors?state=tracked&sort=visibility&domain=all&page=1&per_page=25' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "primary": {
    "id": "10cf756e-ad42-43d7-beda-e8e9cc96cd41",
    "kind": "primary",
    "name": "Acme",
    "domain": "acme.example",
    "aliases": ["Acme AI"],
    "status": "active",
    "competitor_state": null,
    "created_at": "2026-04-12T08:00:00Z",
    "updated_at": "2026-07-28T10:30:00Z"
  },
  "competitors": [
    {
      "id": "63ec8dad-c12f-43c8-89e4-06eb629d0977",
      "kind": "competitor",
      "name": "Northstar",
      "domain": "northstar.example",
      "aliases": ["Northstar AI"],
      "status": "active",
      "competitor_state": "tracked",
      "created_at": "2026-07-02T11:20:00Z",
      "updated_at": "2026-07-30T09:10:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 1,
    "total_pages": 1
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `422` | invalid\_filter \| validation\_failed |  | A state, sort, or domain filter is invalid, or a pagination value is outside the supported range. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Create competitor

`POST /projects/{project_id}/competitors`

Creates a tracked competitor and returns 201 Created. If the normalized domain or case-insensitive name matches an existing dismissed competitor, that record is reactivated and returned with 200 OK instead of creating a duplicate. A tracked competitor requires a domain; aliases accept at most 10 unique values.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `name` | string |  | Competitor display name. Required. |
| `domain` | string |  | Competitor domain. Required for a tracked competitor and normalized before matching. |
| `aliases` | string\[\] |  | Optional alternate names. Maximum: 10 unique values. |

#### Create competitor response envelope

`project_id`:**uuid**`competitor`:**Competitor**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `competitor` | Competitor |  | Created or updated competitor. |

#### Competitor

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique competitor identifier. |
| `kind` | primary \| competitor |  | Entity role. Items in competitors are competitor; primary describes the tracked brand. |
| `name` | string |  | Display name used in prompts, results, and reports. |
| `domain` | string \| null |  | Normalized hostname without a scheme, path, query, leading www, or trailing dot. |
| `aliases` | string\[\] |  | Additional names recognized as this entity. Maximum: 10. |
| `status` | active \| inactive |  | Processing status. Competitors are active exactly when tracked and inactive when suggested or dismissed. |
| `competitor_state` | tracked \| suggested \| dismissed \| null |  | Competitor management lifecycle state. Null for the primary brand. |
| `created_at` | datetime |  | Competitor creation time. |
| `updated_at` | datetime |  | Last competitor update time. |

> **Matching and response status**
>
> Input such as `https://www.northstar.example/pricing` is stored as `northstar.example`. A new record returns `201 Created`; reactivation by normalized domain or case-insensitive name returns `200 OK`.

#### Request and response

```curl
curl --request POST \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Northstar","domain":"https://www.northstar.example/pricing","aliases":["Northstar AI"]}'
```

```json
HTTP/1.1 201 Created

{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "competitor": {
      "id": "63ec8dad-c12f-43c8-89e4-06eb629d0977",
      "kind": "competitor",
      "name": "Northstar",
      "domain": "northstar.example",
      "aliases": ["Northstar AI"],
      "status": "active",
      "competitor_state": "tracked",
      "created_at": "2026-07-02T11:20:00Z",
      "updated_at": "2026-07-30T09:10:00Z"
    }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | domain\_conflict \| own\_domain \| invalid\_state |  | The normalized domain belongs to another active competitor or to the primary brand. |
| `422` | validation\_failed \| limit\_exceeded |  | Required data is absent, aliases exceed 10, or tracked capacity would be exceeded. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Update competitor

`PATCH /projects/{project_id}/competitors/{competitor_id}`

Updates a competitor's name, normalized domain, aliases, or competitor\_state. Omitted fields remain unchanged. Status is derived from competitor\_state and cannot be submitted.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `competitor_id` | uuid | Required | Competitor identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `name` | string |  | Mutable display name. Maximum: 200 characters. |
| `domain` | string |  | Mutable normalized domain. Required whenever competitor\_state is tracked. |
| `aliases` | string\[\] |  | Mutable replacement list of up to 10 unique alternate names. |
| `competitor_state` | tracked \| suggested |  | Mutable lifecycle state. PATCH accepts only tracked or suggested; dismissed is set only by a dismissal or merge operation. |

#### Update competitor response envelope

`project_id`:**uuid**`competitor`:**Competitor**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `competitor` | Competitor |  | Created or updated competitor. |

#### Competitor

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique competitor identifier. |
| `kind` | primary \| competitor |  | Entity role. Items in competitors are competitor; primary describes the tracked brand. |
| `name` | string |  | Display name used in prompts, results, and reports. |
| `domain` | string \| null |  | Normalized hostname without a scheme, path, query, leading www, or trailing dot. |
| `aliases` | string\[\] |  | Additional names recognized as this entity. Maximum: 10. |
| `status` | active \| inactive |  | Processing status. Competitors are active exactly when tracked and inactive when suggested or dismissed. |
| `competitor_state` | tracked \| suggested \| dismissed \| null |  | Competitor management lifecycle state. Null for the primary brand. |
| `created_at` | datetime |  | Competitor creation time. |
| `updated_at` | datetime |  | Last competitor update time. |

> **State restrictions**
>
> PATCH accepts exactly these mutable fields: `name`, `domain`, `aliases`, and `competitor_state`. A tracked competitor requires a domain. Status is set internally to active for tracked competitors and inactive for suggested competitors. Setting the state to suggested frees a tracked slot. Dismissed cannot be supplied to PATCH.

#### Request and response

```curl
curl --request PATCH \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/{competitor_id}' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Northstar AI","aliases":["Northstar","North Star"],"competitor_state":"suggested"}'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "competitor": {
      "id": "63ec8dad-c12f-43c8-89e4-06eb629d0977",
      "kind": "competitor",
      "name": "Northstar AI",
      "domain": "northstar.example",
      "aliases": ["Northstar", "North Star"],
      "status": "inactive",
      "competitor_state": "suggested",
      "created_at": "2026-07-02T11:20:00Z",
      "updated_at": "2026-07-30T09:10:00Z"
    }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | domain\_conflict \| own\_domain \| invalid\_state |  | The normalized domain belongs to another active competitor or to the primary brand. |
| `422` | validation\_failed \| limit\_exceeded |  | Required data is absent, aliases exceed 10, or tracked capacity would be exceeded. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Remove competitor

`DELETE /projects/{project_id}/competitors/{competitor_id}`

Soft-dismisses a competitor while retaining its mentions, rankings, citations, and visibility results.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `competitor_id` | uuid | Required | Competitor identifier. |

> **Result continuity**
>
> Removal does not delete stored results. A later create request with the same normalized domain or case-insensitive name can reactivate the dismissed record.

#### Request and response

```curl
curl --request DELETE \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/{competitor_id}' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
HTTP/1.1 204 No Content
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | invalid\_state |  | The competitor cannot be dismissed from its current state. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Merge competitors

`POST /projects/{project_id}/competitors/merge`

Combines duplicate competitors into one canonical target. Select 2–5 competitors, all in the same lifecycle state.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `primary_id` | uuid |  | Canonical competitor to retain. |
| `duplicate_ids` | uuid\[\] |  | One to four unique duplicate IDs. Together with primary\_id, the selection must contain 2–5 competitors in the same state. |

#### Merge competitors response envelope

`project_id`:**uuid**`competitor`:**Competitor**`merged_ids`:**uuid\[\]**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `competitor` | Competitor |  | Created or updated competitor. |
| `merged_ids` | uuid\[\] |  | Records now pointing to the canonical competitor. |

#### Competitor

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique competitor identifier. |
| `kind` | primary \| competitor |  | Entity role. Items in competitors are competitor; primary describes the tracked brand. |
| `name` | string |  | Display name used in prompts, results, and reports. |
| `domain` | string \| null |  | Normalized hostname without a scheme, path, query, leading www, or trailing dot. |
| `aliases` | string\[\] |  | Additional names recognized as this entity. Maximum: 10. |
| `status` | active \| inactive |  | Processing status. Competitors are active exactly when tracked and inactive when suggested or dismissed. |
| `competitor_state` | tracked \| suggested \| dismissed \| null |  | Competitor management lifecycle state. Null for the primary brand. |
| `created_at` | datetime |  | Competitor creation time. |
| `updated_at` | datetime |  | Last competitor update time. |

> **Merge behavior**
>
> Aliases and retained results are associated with the canonical target. Source records remain dismissed after their aliases are moved to the canonical competitor.

#### Request and response

```curl
curl --request POST \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/merge' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"primary_id":"63ec8dad-c12f-43c8-89e4-06eb629d0977","duplicate_ids":["d732c038-d411-449c-a6cb-351b6d8a2961"]}'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "competitor": {
      "id": "63ec8dad-c12f-43c8-89e4-06eb629d0977",
      "kind": "competitor",
      "name": "Northstar",
      "domain": "northstar.example",
      "aliases": ["Northstar AI"],
      "status": "active",
      "competitor_state": "tracked",
      "created_at": "2026-07-02T11:20:00Z",
      "updated_at": "2026-07-30T09:10:00Z"
    },
  "merged_ids": ["d732c038-d411-449c-a6cb-351b6d8a2961"]
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | mixed\_states \| invalid\_target |  | The selected competitors do not share a state or the primary competitor is invalid. |
| `422` | invalid\_selection |  | Select between 2 and 5 unique competitor IDs. |
| `422` | validation\_failed |  | The merged aliases exceed the supported limit. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Bulk remove competitors

`POST /projects/{project_id}/competitors/bulk_destroy`

Soft-dismisses up to 25 competitors in one request, retains their stored results, and returns the dismissed IDs with 200 OK.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `ids` | uuid\[\] |  | One to 25 unique competitor IDs to dismiss. |

#### Bulk remove competitors response envelope

`project_id`:**uuid**`dismissed_ids`:**uuid\[\]**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `dismissed_ids` | uuid\[\] |  | Competitor IDs dismissed by this request. |

#### Request and response

```curl
curl --request POST \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/bulk_destroy' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"ids":["63ec8dad-c12f-43c8-89e4-06eb629d0977","d732c038-d411-449c-a6cb-351b6d8a2961"]}'
```

```json
HTTP/1.1 200 OK

{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "dismissed_ids": ["63ec8dad-c12f-43c8-89e4-06eb629d0977", "d732c038-d411-449c-a6cb-351b6d8a2961"]
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `422` | invalid\_selection |  | Supply between 1 and 25 unique competitor IDs. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Claim competitor as brand

`POST /projects/{project_id}/competitors/{competitor_id}/claim_as_brand`

Promotes a competitor identity to the primary brand. Its name and aliases are added to the brand aliases, then the competitor is soft-dismissed.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `competitor_id` | uuid | Required | Competitor identifier. |

> **Brand identity update**
>
> Claiming retains stored results, updates the primary brand aliases, and dismisses the competitor so it no longer consumes a tracked slot.

#### Request and response

```curl
curl --request POST \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/{competitor_id}/claim_as_brand' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
HTTP/1.1 204 No Content
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | invalid\_state |  | The competitor cannot be claimed from its current state. |
| `422` | alias\_limit\_exceeded |  | Claiming the competitor would exceed the maximum of 10 brand aliases. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### List suggested competitors

`GET /projects/{project_id}/competitors?state=suggested`

Returns the current suggested competitor dataset for the selected project, capped at 50 items. Suggested items may include visibility-rate and average-rank metrics.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `state` | tracked \| suggested | Optional; Default: suggested | Return competitors in one lifecycle state. |
| `q` | string | Optional | Case-insensitive search across name, domain, and aliases. |
| `domain` | all \| with-domain \| without-domain | Optional; Default: all | Return all competitors or filter by whether a domain is present. |
| `sort` | name-asc \| name-desc \| newest \| visibility \| mentions | Optional; Default: name-asc | Sort by name, creation time, visibility rate, or mention count. |
| `page` | integer | Optional; Default: 1 | The 1-based page number. |
| `per_page` | integer | Optional; Default: 25 | Number of competitors per page. Minimum: 1. Maximum: 50; this matches the maximum suggested dataset size. |

#### List response envelope

`project_id`:**uuid**`primary`:**Competitor**`competitors`:**Competitor\[\]**`pagination`:**Pagination**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `primary` | Competitor |  | Primary tracked brand for comparison. |
| `competitors` | Competitor\[\] |  | Competitors matching the filters. |
| `pagination` | Pagination |  | Pagination metadata for competitors. |

#### Competitor

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique competitor identifier. |
| `kind` | primary \| competitor |  | Entity role. Items in competitors are competitor; primary describes the tracked brand. |
| `name` | string |  | Display name used in prompts, results, and reports. |
| `domain` | string \| null |  | Normalized hostname without a scheme, path, query, leading www, or trailing dot. |
| `aliases` | string\[\] |  | Additional names recognized as this entity. Maximum: 10. |
| `status` | active \| inactive |  | Processing status. Competitors are active exactly when tracked and inactive when suggested or dismissed. |
| `competitor_state` | tracked \| suggested \| dismissed \| null |  | Competitor management lifecycle state. Null for the primary brand. |
| `created_at` | datetime |  | Competitor creation time. |
| `updated_at` | datetime |  | Last competitor update time. |
| `metrics` | CompetitorMetrics (optional) |  | Included only on suggested-list items with qualifying responses; otherwise omitted. |

#### Pagination

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `page` | integer |  | Current 1-based page number. |
| `per_page` | integer |  | Requested page size. Minimum: 1. Maximum: 50. |
| `total` | integer |  | Total competitors matching the applied filters. |
| `total_pages` | integer |  | Total pages at the current per\_page value. |

#### CompetitorMetrics

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `visibility_rate` | number |  | Percentage of eligible responses that mentioned this entity, from 0 through 100. |
| `avg_rank` | number \| null |  | Average 1-based rank when a ranked result included the entity. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors?state=suggested&sort=visibility&domain=all&page=1&per_page=25' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "primary": {
    "id": "10cf756e-ad42-43d7-beda-e8e9cc96cd41",
    "kind": "primary",
    "name": "Acme",
    "domain": "acme.example",
    "aliases": ["Acme AI"],
    "status": "active",
    "competitor_state": null,
    "created_at": "2026-04-12T08:00:00Z",
    "updated_at": "2026-07-28T10:30:00Z"
  },
  "competitors": [
    {
      "id": "a8223436-70dc-4bc8-89fc-99139366711c",
      "kind": "competitor",
      "name": "Orbit Labs",
      "domain": "orbitlabs.example",
      "aliases": [],
      "status": "inactive",
      "competitor_state": "suggested",
      "created_at": "2026-07-21T07:15:00Z",
      "updated_at": "2026-07-30T09:10:00Z",
      "metrics": {
        "visibility_rate": 24.17,
        "avg_rank": 4.2
      }
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 1,
    "total_pages": 1
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `422` | invalid\_filter \| validation\_failed |  | A state, sort, or domain filter is invalid, or a pagination value is outside the supported range. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Track suggested competitor

`PATCH /projects/{project_id}/competitors/{competitor_id}`

Moves a suggested competitor into the tracked set. The suggestion must have a valid normalized domain and a tracked slot must be available.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `competitor_id` | uuid | Required | Competitor identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `name` | string |  | Mutable display name. Maximum: 200 characters. |
| `domain` | string |  | Mutable normalized domain. Required whenever competitor\_state is tracked. |
| `aliases` | string\[\] |  | Mutable replacement list of up to 10 unique alternate names. |
| `competitor_state` | tracked \| suggested |  | Mutable lifecycle state. PATCH accepts only tracked or suggested; dismissed is set only by a dismissal or merge operation. |

> **Required tracking state**
>
> Set `competitor_state` to `tracked`. Status is set to active internally. Any other mutable fields may be updated in the same request.

#### Track suggested competitor response envelope

`project_id`:**uuid**`competitor`:**Competitor**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `competitor` | Competitor |  | Created or updated competitor. |

#### Competitor

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique competitor identifier. |
| `kind` | primary \| competitor |  | Entity role. Items in competitors are competitor; primary describes the tracked brand. |
| `name` | string |  | Display name used in prompts, results, and reports. |
| `domain` | string \| null |  | Normalized hostname without a scheme, path, query, leading www, or trailing dot. |
| `aliases` | string\[\] |  | Additional names recognized as this entity. Maximum: 10. |
| `status` | active \| inactive |  | Processing status. Competitors are active exactly when tracked and inactive when suggested or dismissed. |
| `competitor_state` | tracked \| suggested \| dismissed \| null |  | Competitor management lifecycle state. Null for the primary brand. |
| `created_at` | datetime |  | Competitor creation time. |
| `updated_at` | datetime |  | Last competitor update time. |

#### Request and response

```curl
curl --request PATCH \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/{competitor_id}' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"competitor_state":"tracked"}'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "competitor": {
      "id": "a8223436-70dc-4bc8-89fc-99139366711c",
      "kind": "competitor",
      "name": "Orbit Labs",
      "domain": "orbitlabs.example",
      "aliases": [],
      "status": "active",
      "competitor_state": "tracked",
      "created_at": "2026-07-21T07:15:00Z",
      "updated_at": "2026-07-30T09:10:00Z"
    }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | domain\_conflict \| own\_domain \| invalid\_state |  | The normalized domain belongs to another active competitor or to the primary brand. |
| `422` | validation\_failed \| limit\_exceeded |  | Required data is absent, aliases exceed 10, or tracked capacity would be exceeded. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Dismiss suggested competitor

`DELETE /projects/{project_id}/competitors/{competitor_id}`

Soft-dismisses a suggested competitor so it is excluded from the active suggestion queue while retaining its discovery evidence.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `competitor_id` | uuid | Required | Competitor identifier. |

#### Request and response

```curl
curl --request DELETE \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/competitors/{competitor_id}' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
HTTP/1.1 204 No Content
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "domain", "message": "must be a valid hostname" }],
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `401` | invalid\_api\_key |  | Authorization bearer token is absent or invalid. |
| `403` | forbidden |  | The API key lacks the required competitor capability or cannot access this scope. |
| `404` | not\_found |  | Project, location, or competitor was not found. |
| `409` | invalid\_state |  | The competitor cannot be dismissed from its current state. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |
