Create competitor
Adds a competitor to the set this brand is tracked against. The new competitor is matched against future answers by its name, its aliases and its domain, and appears in List competitors and the rankings immediately.
Creates the same row the Add competitor dialog does, including its defaults: active, manually added, a direct competitor, and matched on the whole registrable domain.
Requires the write:competitors scope, and the workspace must be enabled for API writes.
POST /v1/brands/{brand_id}/competitorsAuthorization
| Header | Type | Required |
|---|---|---|
Authorization | Bearer asky_sk_… | required |
Idempotency-Key | string | required |
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
brand_id | uuid | required | From List brands |
Query parameters
None.
Any parameter you pass returns 400 invalid_request naming the offending key.
Body
application/json
| Field | Type | Required | Description |
|---|---|---|---|
name | string, 1–200 chars | required | Display name, also matched against answers |
primary_url | string, up to 2048 chars | required | The competitor’s main URL, http or https, pointing at a domain |
aliases | string[], up to 50 | optional | Other names to match on. Blanks and duplicates are dropped. Defaults to [] |
relation | string | optional | One of direct_competitor, indirect_competitor, partner. Defaults to direct_competitor |
match_case_sensitive | boolean | optional | Match name and aliases case-sensitively. Defaults to false |
match_require_domain | boolean | optional | Only count a page mention when a domain signal is present. Defaults to false |
Request
cURL
curl --request POST \
--url 'https://api.askylabs.com/v1/brands/242546de-4cee-4a7d-952e-f6aa60c63ef8/competitors' \
--header 'Authorization: Bearer <api-key>' \
--header 'Idempotency-Key: <unique-per-attempt>' \
--header 'Content-Type: application/json' \
--data '{"name":"Profound","primary_url":"https://tryprofound.com","aliases":["Try Profound"]}'Response
200
{
"data": {
"competitor": {
"id": "c4a7e1b2-38d9-4f60-b5ac-2d90e7f14b83",
"name": "Profound",
"primary_url": "https://tryprofound.com",
"domain": "tryprofound.com",
"relation": "direct_competitor",
"aliases": ["Try Profound"],
"is_active": true,
"is_manual": true,
"match_case_sensitive": false,
"match_require_domain": false,
"match_mode": "apex"
}
},
"request_id": "req_9e14c0bd72a34f81b5de"
}Response fields
| Field | Type | Description |
|---|---|---|
data.competitor.id | uuid | required. Use it with Get competitor or as a competitor_id filter |
data.competitor.name | string | required |
data.competitor.primary_url | string | null | required |
data.competitor.domain | string | null | required. Derived from primary_url. See the note below on when it is null |
data.competitor.relation | string | required |
data.competitor.aliases | string[] | required |
data.competitor.is_active | boolean | required |
data.competitor.is_manual | boolean | required. Always true for competitors created here |
data.competitor.match_case_sensitive | boolean | required |
data.competitor.match_require_domain | boolean | required |
data.competitor.match_mode | string | required. Always apex for competitors created here |
request_id | string | required. Also returned as the x-request-id header |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | Unknown field, missing name or primary_url, a URL that is not http/https or does not point at a domain, an unknown relation, or more than 50 aliases |
| 401 | invalid_token | The key is missing, malformed, or does not exist |
| 403 | insufficient_scope | The key does not carry write:competitors |
| 403 | plan_required | This workspace is not enabled for API writes |
| 404 | not_found | The brand does not exist, or this key is not allowed to reach it |
| 429 | rate_limited | Over 60 requests per minute |
Notes
primary_url is required, unlike in the read shape. A competitor added by hand must have a URL:
it is how citations and page mentions are attributed. Only auto-discovered competitors may exist
without one.
domain can come back null, and that is not an error. It is derived from
primary_url, and if another competitor on this brand already claims the same domain, the field is
left empty rather than the request failing. Matching still works, because the domain is derived from
primary_url when answers are scored. A null here is the signal that you may be tracking the same
company twice.
Names are not unique. Two competitors on one brand may share a name. If you are syncing from an
external system, deduplicate on domain rather than on name.
Some fields are deliberately not settable. regex_pattern, extra_apex_domains,
owned_url_scopes and match_mode all change how citations are attributed, retroactively across
all history, so they are not part of creating a competitor. Set them in the app.
Idempotency-Key is required. Reuse it when retrying an attempt and the original response is
replayed rather than a second competitor being created. See Idempotency.