Projects
A project is the top-level container for AI Visibility monitoring. Each project has a primary brand, a set of AI providers, and a schedule that controls automatic prompt execution.
List projects
Retrieve all AI Visibility projects for an account. Results include metadata like topic and prompt counts, configured AI providers, and the primary brand domain.
query GetAiVisibilityProjects($accountId: ObjectID!) {
getAiVisibilityProjects(
accountId: $accountId
first: 10
) {
nodes {
id
rawId
name
totalTopics
totalPrompts
aiProviderTypes
primaryBrandDomain
primaryBrandRawId
scheduleCadence
nextScheduleRunAt
autoCrawlEnabled
freshnessThresholdDays
country
language
createdAt
updatedAt
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
You can search projects by name using the search parameter:
query SearchAiVisibilityProjects($accountId: ObjectID!, $search: String!) {
getAiVisibilityProjects(
accountId: $accountId
first: 10
search: $search
) {
nodes {
id
name
primaryBrandDomain
}
totalCount
}
}
Sorting
Projects can be sorted by: name, primaryBrandDomain, totalTopics, totalPrompts.
Get a single project
Fetch a specific project by its ID:
query GetAiVisibilityProject(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
) {
getAiVisibilityProject(
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
) {
id
rawId
name
scheduleCadence
nextScheduleRunAt
autoCrawlEnabled
freshnessThresholdDays
primaryBrand {
id
rawId
name
}
aiProviders {
id
name
type
}
createdAt
updatedAt
}
}
Create a project
Creating a project requires a name, brand name, and brand domain. The primary brand is automatically created as an own type brand.
mutation CreateAiVisibilityProject(
$accountId: ObjectID!
$name: String!
$brandName: String!
$brandDomain: String!
$scheduleCadence: AiVisibilityScheduleCadence
$brandThemeSentimentCaptureEnabled: Boolean!
$country: String
$language: String
) {
createAiVisibilityProject(
input: {
accountId: $accountId
name: $name
brandName: $brandName
brandDomain: $brandDomain
scheduleCadence: $scheduleCadence
brandThemeSentimentCaptureEnabled: $brandThemeSentimentCaptureEnabled
country: $country
language: $language
}
) {
aiVisibilityProject {
id
rawId
name
scheduleCadence
nextScheduleRunAt
brandThemeSentimentCaptureEnabled
country
language
createdAt
}
}
}
Project limit
Your subscription caps how many projects an account may hold. Account.aiVisibilityProjectsLimit is that cap and Account.aiVisibilityProjectsCount is the number of projects already created, so gate a "create project" action on those two fields rather than re-deriving the limit: createAiVisibilityProject enforces exactly these two numbers and fails with AI_VISIBILITY_PROJECTS_LIMIT_REACHED once they meet. Deleted projects are not counted.
aiVisibilityProjectsLimit is null when the account has no project cap, and the mutation then allows creation without limit, so test for it before comparing:
aiVisibilityProjectsLimit == null || aiVisibilityProjectsCount < aiVisibilityProjectsLimit
Comparing a null limit numerically would disable creation on exactly the accounts that have no cap.
Schedule cadence
The scheduleCadence field controls how often prompts are automatically sent to AI providers:
| Value | Description |
|---|---|
Daily | Every day (default) |
Weekly | Once per week |
EveryTwoWeeks | Every two weeks |
Monthly | Once per month |
EveryTwoMonths | Every two months |
Quarterly | Once every three months |
The schedule cadence also affects the maximum number of prompts allowed in a project. More frequent cadences allow fewer prompts per project.
AI providers
By default, all AI providers included in your subscription are linked to the project. You can optionally specify aiVisibilityAiProviderIds to select a subset of providers.
Market: country and language
A project can carry a market: country (ISO 3166-1 alpha-2, e.g. GB) and language (ISO 639-1, e.g. en). One project targets one market; to compare the same prompts across the US and the UK, create two projects.
Both fields are optional and three-state on createAiVisibilityProject:
| Input | Stored value |
|---|---|
| Field omitted | Copied from the account's defaultCountry / defaultLanguage, if set |
Explicit null | null (worldwide / unspecified), even when the account has a default |
A value ("gb") | Validated and normalised (GB, en) |
The value is copied at creation time. Changing an account default later never rewrites existing projects. See Account settings.
When language is omitted and the account has no defaultLanguage, the project does not stay language-less: the language is detected from the brand domain you create the project with, and stored. Detection is best-effort and time-boxed, so a site that cannot be read, or that establishes no clear language, leaves language as null. It also runs only for requests authenticated as a user: a service-account API key creates the project without detection, so pass language explicitly on those calls. An explicit null is never detected over -- it keeps its meaning of "unspecified on purpose". Read language back after creating a project rather than assuming the value you sent.
What the fields do:
countryis copied onto every prompt created without an explicitcountry(bothcreateAiVisibilityPromptandcreateAiVisibilityTopicsBulk). An explicit promptcountry, includingnullfor worldwide, always wins, so accounts that set country per prompt are unaffected. Prompt runs keep using the prompt's stored country; the existingcountryread filter is unchanged.languagesteersgenerateAiVisibilitySuggestedTopics,generateAiVisibilitySuggestedPromptsandgenerateAiVisibilityTopicMetadata, which also usecountryfor local context (competitors, spelling, regulations). Prompts themselves carry no language.
Generation never changes a project's language: it uses what the project carries. A project with no language -- one created before detection existed, or one whose detection found nothing -- generates as it always did, so set language on those projects yourself if the output language matters.
Unknown or malformed codes are rejected as input validation errors. Country is case-insensitive and stored upper-case; language must be lower-case.
To build a country or language picker, list the accepted codes with their English names. Neither query requires authentication:
query GetAiVisibilityMarketCodes {
aiVisibilityCountries {
code
name
}
aiVisibilityLanguages {
code
name
}
}
Country codes apply to account defaults, projects, prompts, and the free scan. Language codes apply to account defaults, projects, and the free scan; prompts carry no language.
Update a project
Update a project's name, schedule cadence, market, or content evaluation settings:
mutation UpdateAiVisibilityProject(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
$name: String
$scheduleCadence: AiVisibilityScheduleCadence
$autoCrawlEnabled: Boolean
$freshnessThresholdDays: Int
$brandThemeSentimentCaptureEnabled: Boolean
$country: String
$language: String
) {
updateAiVisibilityProject(
input: {
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
name: $name
scheduleCadence: $scheduleCadence
autoCrawlEnabled: $autoCrawlEnabled
freshnessThresholdDays: $freshnessThresholdDays
brandThemeSentimentCaptureEnabled: $brandThemeSentimentCaptureEnabled
country: $country
language: $language
}
) {
aiVisibilityProject {
id
name
scheduleCadence
autoCrawlEnabled
freshnessThresholdDays
brandThemeSentimentCaptureEnabled
country
language
}
}
}
Omitted fields are left unchanged. For country and language, pass null to clear the value. Changing the market only affects prompts created afterwards and future generation; existing prompts and their runs are not rewritten, so retargeting a market is normally a new project.
Content evaluation settings
autoCrawlEnabled-- Whentrue, cited pages from your brand's domains are automatically crawled and evaluated after each prompt runfreshnessThresholdDays-- Number of days (1-90, default 7) before a previously crawled URL is considered stale and re-crawled
Theme sentiment capture
brandThemeSentimentCaptureEnabled-- Whentrue, the project requests per-theme brand sentiment capture on its prompt runs. Off by default. This is a request, not a guarantee: capture is performed by the prompt tracker, which may not honour the request, so theme sentiment data can remain empty even while the setting is enabled. An empty sentiment series is therefore a normal state, not an error. The setting can also be passed oncreateAiVisibilityProject, so a project can opt in before its first prompt runs.
Changing scheduleCadence may reduce the allowed prompt count. If the project's active prompt count exceeds the new per-project limit, the update will fail with AI_VISIBILITY_PROMPTS_LIMIT_REACHED. A project sitting exactly at the new limit can still change cadence, and the account-wide limit is not checked here because cadence does not affect it -- see Create a prompt.
Check run capacity before adding prompts
getAiVisibilityRunUsage models how a project's configuration consumes the account's AI Visibility capacity, so a client can tell how many prompts can still be added before saving. Pass a draft promptCount, scheduleCadence or aiProviderTypes to model a change that is not saved yet; each defaults to the project's current value, and previewAiProviderType models enabling one more provider.
query GetAiVisibilityRunUsage(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
$promptCount: Int
$scheduleCadence: AiVisibilityScheduleCadence
) {
getAiVisibilityRunUsage(
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
promptCount: $promptCount
scheduleCadence: $scheduleCadence
) {
promptCount
effectivePromptsLimit
accountPromptsLimit
accountPromptsRoom
roomToAddPrompts
bindingConstraint
bindingPoolKey
overLimit
pools {
key
type
totalRuns
availableRuns
overLimit
}
}
}
The response weighs three constraints and reports the tightest one:
-
Credit pools (
pools) -- the provider-specific and shared credit buckets the selected providers draw on, with demand normalised to the fixedplanningPeriodDayswindow.bindingPoolKeynames the pool when it is the binding constraint.Normalising can leave a project owing a fraction of a run: at
cadenceFactor1/3, a quarterly prompt costs a third of a run over a 30-day window. Demand is summed across every project and provider drawing on a pool before being rounded up, so three quarterly prompts cost one run rather than three. Rounding once per pool meansusedRuns,projectedRuns,availableRuns,overageRunsand each entry insegmentsare whole runs that add up to the pool total, whileoverLimitandroomToAddPromptsfollow the unrounded demand. A configuration with fractional demand -- any cadence other than daily or monthly -- can therefore report slightly lower usage than the sum of its parts suggests. -
Per-project prompt limit (
effectivePromptsLimit) -- the project's own cap, from the subscription, provider count and schedule cadence. -
Account-wide prompt limit (
accountPromptsLimit,accountPromptsRoom) -- the budget shared by every project on the account and the part of it this project can still use (the limit minus active prompts on the account's other projects). Both arenullwhen the account has no account-wide limit. See Create a prompt for how the two prompt limits are enforced.
roomToAddPrompts is the number of prompts the modelled configuration can still add under the binding constraint, and bindingConstraint says which one binds: Pool, ProjectPromptLimit or AccountPromptLimit. overLimit is true when the modelled prompt count already exceeds any of them, and previewCanBeEnabled answers whether a previewAiProviderType would still fit.
Tags
Projects can be grouped with the account's tags — the same pool that covers Analyze projects and
Protect test suites. AiVisibilityProject.tags returns a project's tags, getAiVisibilityProjects
takes a tagIds filter, and createAiVisibilityProject takes accountTagIds. Applying tags requires
the account's admin role, while creating a project only requires editor, so an editor passing
accountTagIds creates nothing.
See Project Tags for the tag lifecycle and the linking mutations.
Delete a project
Soft-deletes a project. This is a destructive operation.
mutation DeleteAiVisibilityProject(
$accountId: ObjectID!
$aiVisibilityProjectId: ObjectID!
) {
deleteAiVisibilityProject(
input: {
accountId: $accountId
aiVisibilityProjectId: $aiVisibilityProjectId
}
) {
aiVisibilityProject {
id
}
}
}
Schema reference
AiVisibilityProject-- Project typeCreateAiVisibilityProjectInput-- Creation inputUpdateAiVisibilityProjectInput-- Update inputAiVisibilityScheduleCadence-- Schedule optionsAiVisibilityRunUsage-- Run capacity model returned bygetAiVisibilityRunUsage(schema page appears once the query is released)AiVisibilityRunUsageBindingConstraint-- Which limit bindsAiVisibilityAccountSettings-- Account defaults new projects inherit (see Account settings)