Create Domain
Create a brand/domain and its starting prompts so analysis begins immediately.
curl --request POST \
--url https://api.aiclicks.io/api/v1/domains \
--header 'Authorization: Bearer ak_live_xxx' \
--header 'Content-Type: application/json' \
--data '{
"team_id": "2b1f8c3d-4a5e-4f10-9c21-3d8e5a6b7c90",
"name": "Acme Inc.",
"website": "acme.com",
"country": "US",
"prompts": [
{ "text": "best CRM for small business", "topics": ["CRM"], "tags": ["priority"] },
{ "text": "Salesforce vs HubSpot", "topics": ["Comparisons"] }
]
}'
import httpx, os
resp = httpx.post(
"https://api.aiclicks.io/api/v1/domains",
headers={"Authorization": f"Bearer {os.environ['AICLICKS_API_KEY']}"},
json={
"team_id": "2b1f8c3d-4a5e-4f10-9c21-3d8e5a6b7c90",
"name": "Acme Inc.",
"website": "acme.com",
"country": "US",
"prompts": [
{"text": "best CRM for small business", "topics": ["CRM"], "tags": ["priority"]},
{"text": "Salesforce vs HubSpot", "topics": ["Comparisons"]},
],
},
)
resp.raise_for_status()
data = resp.json()["data"]
print(data["domain_id"])
for q in data["queries"]:
print(q["id"], q["text"])
const resp = await fetch("https://api.aiclicks.io/api/v1/domains", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AICLICKS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
team_id: "2b1f8c3d-4a5e-4f10-9c21-3d8e5a6b7c90",
name: "Acme Inc.",
website: "acme.com",
country: "US",
prompts: [
{ text: "best CRM for small business", topics: ["CRM"], tags: ["priority"] },
{ text: "Salesforce vs HubSpot", topics: ["Comparisons"] },
],
}),
});
const { data } = await resp.json();
console.log(data.domain_id);
for (const q of data.queries) console.log(q.id, q.text);
{
"data": {
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"queries": [
{
"id": "21c1f9aa-9b6d-4e3a-8a31-2b0c0e1f2a3b",
"text": "best CRM for small business",
"topic_id": "a7d0e2c4-1b3f-4d5a-9e6b-7c8d9e0f1a2b",
"topic_ids": ["a7d0e2c4-1b3f-4d5a-9e6b-7c8d9e0f1a2b"]
},
{
"id": "32d2e8bb-aa7e-5f4b-9b42-3c1d1f2a3b4c",
"text": "Salesforce vs HubSpot",
"topic_id": "b8e1f3d5-2c4a-4e6b-8f7c-9d0e1f2a3b4c",
"topic_ids": ["b8e1f3d5-2c4a-4e6b-8f7c-9d0e1f2a3b4c"]
}
]
},
"generated_at": "2026-06-17T10:00:11.218Z"
}
{
"detail": "Provide `prompts` (each {text, topics?, tags?}) or set `generate_prompts` true, so there is something to analyze."
}
{
"detail": "Invalid or revoked API key"
}
{
"detail": "API key not authorized for this team. This API key is scoped to a single team. Create and use a master key (scoped to all teams) to manage projects and other teams."
}
{
"detail": "Team not found"
}
{
"detail": [
{
"type": "value_error",
"loc": ["body", "prompts", 0, "text"],
"msg": "Value error, Prompt text cannot be empty"
}
]
}
{
"detail": "Too many requests. Please try again later."
}
Creates a new domain (brand) under a team and seeds the prompts that drive every analysis run. You must give the domain something to analyze: either supply your own prompts, or set generate_prompts: true to have AIClicks generate a starter set. A domain with neither would sit in the "analyzing" state forever, so the endpoint rejects that up front with a 400.
The call returns as soon as the domain row and its query IDs exist — the domain is inserted synchronously, then prompt/topic/tag writes finish in the background. The returned queries carry the real IDs you can reference immediately.
team_id is required. Use `` to discover which teams the calling key can access (a master key spans multiple teams; a team key is pinned to one).
This is a billed write. Creating a domain consumes one domain slot from the team's plan, and each prompt (whether supplied or generated) draws from the team's remaining prompt balance. Analysis starts automatically once prompts exist.
To have AIClicks generate the starting prompts instead, drop prompts and send generate_prompts: true:
{
"team_id": "2b1f8c3d-4a5e-4f10-9c21-3d8e5a6b7c90",
"name": "Acme Inc.",
"website": "acme.com",
"generate_prompts": true
}
Authorizations
Your API key formatted as Bearer ak_live_<your-key>. Create one in the dashboard under Settings → Developers. The key's team scope must include team_id.
Must be application/json.
Optional UUID for log correlation. If omitted, we generate one and echo it back in the response.
Body parameters
UUID of the team that will own the domain. The caller must be a member of this team, and (for a team-scoped key) it must match the key's team. Find teams via ``.
Display name of the brand, e.g. Acme Inc.. Trimmed; max 200 characters; may not contain < or >.
The brand's website. Max 255 characters; may not contain < or >.
ISO country hint for analysis. When provided, it overrides the country AIClicks infers for the domain. Omit to let AIClicks detect it.
Prompts to track from the start. Each item is { text, topics?, tags? }. Supply this OR``, never both — sending both is a 400. Each supplied prompt draws one prompt credit.
When true, AIClicks generates a starter set of prompts for the domain instead of using prompts. Supply this OR``, never both. Generated prompts still draw from the team's prompt balance.
Optional geo-targeting state identifiers (opaque pass-through, e.g. "US||US-CA|California"). User-supplied values win over AIClicks-inferred geography.
Optional geo-targeting city identifiers (opaque pass-through). Same override behavior as states.
Response
The creation result.
UUID of the newly created domain. Use it as {domain_id} in every other endpoint immediately — the row is inserted before the response returns.
The prompts that were seeded — one entry per supplied or generated prompt. Empty only if neither prompts nor generation applied (which the 400 guard normally prevents).
ISO-8601 timestamp of when the server produced this response.
Response headers
| Header | Description |
|---|---|
X-Request-Id | Unique request id. Echoes incoming if you set one. |
X-RateLimit-Limit | Max requests per minute for this key. |
X-RateLimit-Remaining | Requests remaining in current minute. |
X-RateLimit-Reset | Unix epoch seconds when the window resets. |
Side effects
- Domain slot consumed. One domain is deducted from the team's plan allowance (
403/400if none remain). - Prompt credits consumed. Each supplied or generated prompt draws from the team's remaining prompt balance.
- Analysis starts immediately. As soon as the seeded prompts land, the domain begins its first analysis run — no separate "start" call is needed.
- List caches invalidated. The
GET /api/v1/domainscache is purged so the new domain shows up right away. - Creator allowlist updated. The calling user is granted access to the new domain in their team-member allowlist.
The domain row and query IDs are written synchronously, but topic and tag associations complete in the background. If you immediately re-read prompts/topics, allow a moment for the background write to settle.
Errors
One of: neither prompts nor generate_prompts supplied ("Provide prompts ... so there is something to analyze."); both supplied ("Provide promptsor setgenerate_prompts true, not both ..."); the team is out of domain slots ("You have reached the maximum number of domains for your subscription tier"); or not enough prompt credits for the requested prompts ("Not enough remaining prompts. Need N prompts but only have M remaining. Please upgrade your plan or purchase more prompts.").
Missing, malformed, or revoked API key.
The API key is scoped to a different team ("API key not authorized for this team. ..."), the caller is not a member of team_id ("Access denied: not a member of this team"), or the team lacks API access ("The Starter plan does not include API access. Upgrade to Pro at https://app.aiclicks.io/billing to enable it.").
team_id is not a valid UUID — "Team not found".
Body failed validation — most commonly a blank/whitespace prompt text ("Prompt text cannot be empty"), text over 500 characters ("Prompt text must be 500 characters or fewer"), text containing </> ("Prompt text may not contain '<' or '>'"), or a missing required field (team_id, name, website).
Rate limit exceeded. Inspect the Retry-After header for how long to wait.