The prompt text to track. Trimmed; must be non-blank; max 500 characters; may not contain < or >. A blank/whitespace value returns 422.
Add Prompt
Add a single tracked prompt (with optional topics and tags) to a domain.
curl --request POST \
--url https://api.aiclicks.io/api/v1/prompts \
--header 'Authorization: Bearer ak_live_xxx' \
--header 'Content-Type: application/json' \
--data '{
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"text": "best CRM for small business",
"topic_ids": ["a7d0e2c4-1b3f-4d5a-9e6b-7c8d9e0f1a2b"],
"tag_ids": ["c9f2a4b6-3d5e-4f7a-8b9c-0d1e2f3a4b5c"]
}'
import httpx, os
resp = httpx.post(
"https://api.aiclicks.io/api/v1/prompts",
headers={"Authorization": f"Bearer {os.environ['AICLICKS_API_KEY']}"},
json={
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"text": "best CRM for small business",
"topic_ids": ["a7d0e2c4-1b3f-4d5a-9e6b-7c8d9e0f1a2b"],
"tag_ids": ["c9f2a4b6-3d5e-4f7a-8b9c-0d1e2f3a4b5c"],
},
)
resp.raise_for_status()
prompt = resp.json()["data"]
print(prompt["id"], prompt["text"])
const resp = await fetch("https://api.aiclicks.io/api/v1/prompts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AICLICKS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
domain_id: "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
text: "best CRM for small business",
topic_ids: ["a7d0e2c4-1b3f-4d5a-9e6b-7c8d9e0f1a2b"],
tag_ids: ["c9f2a4b6-3d5e-4f7a-8b9c-0d1e2f3a4b5c"],
}),
});
const { data } = await resp.json();
console.log(data.id, data.text);
{
"data": {
"id": "21c1f9aa-9b6d-4e3a-8a31-2b0c0e1f2a3b",
"text": "best CRM for small business",
"estimated_search_volume": 8100,
"created_at": "2026-06-17T10:00:11.118Z"
},
"generated_at": "2026-06-17T10:00:11.218Z"
}
{
"detail": "Not enough remaining prompts. Please upgrade your plan or purchase more prompts."
}
{
"detail": "Invalid or revoked API key"
}
{
"detail": "Access denied: User is not a member of the team that owns this domain"
}
{
"detail": "Domain not found"
}
{
"detail": "A prompt with the same text already exists in this domain."
}
{
"detail": [
{
"type": "value_error",
"loc": ["body", "text"],
"msg": "Value error, Prompt text cannot be empty"
}
]
}
{
"detail": "Too many requests. Please try again later."
}
Adds one prompt (a tracked query) to an existing domain. The new prompt joins the domain's analysis set immediately — it feeds the same visibility metrics, citation reports, and leaderboards as every other prompt.
domain_id is required. Use to find domains the calling key can access, to resolve topic names to IDs, and `` to resolve tag names to IDs.
This is a billed write. Adding a prompt consumes one prompt credit from the domain's team. If the team is out of credits the call returns 400 and nothing is created.
Authorizations
Your API key formatted as Bearer ak_live_<your-key>. Create one in the dashboard under Settings → Developers. The key must be scoped to the team that owns domain_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 domain to add the prompt to. Find domains via ``. A malformed UUID returns 404.
Optional list of topic UUIDs to group this prompt under. Resolve names to IDs via ``. Defaults to [] (no topic).
Optional list of tag UUIDs to attach. Resolve names to IDs via ``. This field has three distinct behaviors:
- Omitted or`` — the domain's default tags are auto-assigned.
- `` — no tags are attached.
- A list of IDs — those tags are attached; IDs that don't belong to the domain are silently dropped (the prompt is already created and the credit already spent, so unusable IDs are ignored rather than failing the request).
Response
The created prompt. Internal columns (analyzable, keyword, domain_id) are deliberately not exposed.
UUID of the new prompt.
The prompt text as stored (trimmed).
Estimated monthly search volume for the prompt. null if not yet computed.
ISO-8601 timestamp of when the prompt was created.
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
- One prompt credit consumed from the domain's team.
- Prompt joins analysis immediately — it participates in the next analysis run and all downstream metrics.
- Topic and tag associations written per
topic_ids/tag_ids(see the tri-state behavior above). - Prompt list cache invalidated — the
GET /api/v1/promptscache for this domain is purged, plus the domain's tag/topic list caches, so the new prompt appears right away.
Errors
The team has no prompt credits left — "Not enough remaining prompts. Please upgrade your plan or purchase more prompts."
Missing, malformed, or revoked API key.
The caller is not a member of the domain's team ("Access denied: User is not a member of the team that owns this domain"), the API key is scoped to a different team ("API key not authorized for this domain. ..."), 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.").
domain_id is not a valid UUID — "Domain not found". A well-formed UUID that the caller cannot access returns 403, not 404.
An active prompt with the same text already exists on this domain — "A prompt with the same text already exists in this domain." No credit is consumed.
Body failed validation — blank/whitespace 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.
Rate limit exceeded. Inspect the Retry-After header for how long to wait.