Add Competitors in Bulk
Track up to 50 competitors for a domain in one call. Send a list, or take the top candidates from competitor discovery.
curl --request POST \
--url 'https://api.aiclicks.io/api/v1/competitors/bulk' \
--header 'Authorization: Bearer ak_live_xxx' \
--header 'Content-Type: application/json' \
--data '{
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"competitors": [
{ "competitor_domain": "acmerival.com", "competitor_name": "Acme Rival" },
{ "competitor_domain": "https://www.globex.example/", "competitor_name": "Globex", "competitor_variations": ["Globex Corp"] }
]
}'
import httpx, os
resp = httpx.post(
"https://api.aiclicks.io/api/v1/competitors/bulk",
json={
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"from_discovery": 5,
},
headers={"Authorization": f"Bearer {os.environ['AICLICKS_API_KEY']}"},
)
resp.raise_for_status()
result = resp.json()["data"]
print(len(result["added"]), "added,", len(result["skipped"]), "skipped")
const resp = await fetch("https://api.aiclicks.io/api/v1/competitors/bulk", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AICLICKS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
domain_id: "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
competitors: [
{ competitor_domain: "acmerival.com", competitor_name: "Acme Rival" },
],
}),
});
const { data } = await resp.json();
console.log(data.remaining_slots);
{
"data": {
"added": [
{
"id": "f6a7b8c9-3333-4eee-8fff-000000000002",
"created_at": "2026-11-09T09:20:02.114Z",
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"name": "Globex",
"website": "globex.example",
"is_dedicated": true,
"active": true,
"updated_at": "2026-11-09T09:20:02.114Z",
"variations": ["Globex Corp"]
}
],
"promoted": [
{
"id": "f6a7b8c9-3333-4eee-8fff-000000000001",
"created_at": "2026-08-18T10:00:11.218Z",
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"name": "Acme Rival",
"website": "acmerival.com",
"is_dedicated": true,
"active": true,
"updated_at": "2026-11-09T09:20:02.087Z",
"variations": []
}
],
"skipped": [
{ "competitor": "initech.example", "reason": "already tracked" }
],
"remaining_slots": 4
},
"generated_at": "2026-11-09T09:20:02.201Z",
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3"
}
{
"detail": "Provide either `competitors` or `from_discovery`, not both or neither."
}
{
"detail": "Invalid or revoked API key"
}
{
"detail": "API access is not enabled for this team. Contact support@aiclicks.io."
}
{
"detail": "Domain not found"
}
{
"detail": "Too many requests. Please try again later."
}
This endpoint tracks many competitors in one request. Every competitor that it adds is a tracked competitor (is_dedicated: true, active: true).
Use one of two inputs. Send them alone, not together:
competitors: a list of up to 50 competitors that you choose.from_discovery: a number from 1 to 50. The endpoint takes that number of top candidates fromGET /api/v1/competitor-discovery.
If a competitor with the same website is already on the domain but is not tracked, the endpoint promotes that row. It does not make a duplicate. If the competitor is already tracked, the endpoint skips it.
Each added or promoted competitor uses one slot of the dedicated-competitor limit of the domain. When no slots remain, the endpoint skips the remaining competitors with the reason "competitor limit reached". The request does not fail.
Authorizations
Your API key in the format Bearer ak_live_<your-key>. Create a key in the dashboard under Settings → Developers.
An optional UUID for log correlation. If you do not send one, the server makes one and returns it in the response.
Body parameters
The UUID of the domain. Find domains with GET /api/v1/domains.
The competitors to track, at most 50. Do not send this field together with from_discovery.
The number of top discovery candidates to track, from 1 to 50. The order is the same as GET /api/v1/competitor-discovery. Do not send this field together with competitors.
Response
The result for each competitor in the request.
The new competitor rows. Each row has the same fields as the competitor object in POST /api/v1/competitors.
The existing rows that the endpoint changed to tracked. Each row has the same fields as added.
The competitors that the endpoint did not add.
The number of dedicated-competitor slots that remain on the domain after this request.
The ISO-8601 time when the server made this response.
The domain from the request.
Response headers
| Header | Description |
|---|---|
X-Request-Id | The request id. If you sent one, the server returns it. |
X-RateLimit-Limit | The maximum number of requests per minute for this key. |
X-RateLimit-Remaining | The number of requests that remain in this minute. |
X-RateLimit-Reset | The Unix time in seconds when the window starts again. |
Caching
This endpoint is not cached. A successful request clears the cached competitor data of the domain.
Errors
You sent both competitors and from_discovery, or you sent neither. If competitors has more than 50 items or from_discovery is out of range, the endpoint returns 422.
The API key is missing, malformed or revoked.
The developer_access flag of the team is off. Or the domain is not in the scope of your key, or your allowed_domains list does not include it.
The domain_id is malformed or does not exist, or your user is not a member of its team.
You sent too many requests. Read the Retry-After header to know how long to wait.