# API keys
> Workspace keys for scripts and clients without a browser. Format, headers, limits, and what a key cannot do.
Source: https://docs.indexzero.site/agents/api-keys
An API key authenticates a request to the MCP endpoint as your workspace, without an OAuth flow. Use one for scripts, CI, server-side agents, or any client that does not implement OAuth for remote MCP servers.
## Creating a key [#creating-a-key]
### Open AI & MCP [#open-ai--mcp]
In the app, open **AI & MCP** from the sidebar. Creating keys requires a plan with MCP access; on the Free plan the page shows the upgrade prompt instead.
### Name it and create [#name-it-and-create]
Give the key a name (up to 80 characters, "CI pipeline" style) so you can tell keys apart later. Keys created in the app do not expire; delete a key to revoke it.
### Copy it now [#copy-it-now]
The key is shown **once**, at creation. Only a hash is stored; if you lose it, delete it and create another.
Keys look like `iz_` followed by a random string. The list on the page shows each key's name, the first characters, when it was created, and when it was last used, and lets you delete it.
## Sending it [#sending-it]
Either header form works:
```
x-api-key: iz_your_key_here
```
```
Authorization: Bearer iz_your_key_here
```
The `iz_` prefix is what identifies the value as an IndexZero key. A bearer token without it is treated as an OAuth access token instead.
## What a key can and cannot do [#what-a-key-can-and-cannot-do]
* A key reaches **only** `https://app.indexzero.site/mcp`. It is verified directly and never exchanged for a session, so it cannot reach account, billing, or dashboard endpoints even if leaked.
* A key acts as the **whole workspace** with every MCP scope. There are no per-tool scopes on keys; use separate keys per integration so one can be revoked without the others.
* Deleting a key takes effect immediately.
## Rate limits [#rate-limits]
Keys are limited to **500 requests per minute**. Past that, the endpoint returns HTTP 429 with a `Retry-After` header in seconds:
```json
{ "error": "rate_limited", "error_description": "..." }
```
## Errors [#errors]
| Status | Body | Meaning |
| ------ | -------------------------------------------------------------------- | ------------------------------------------------- |
| 401 | `{"error":"invalid_api_key", ...}` | The key is unknown, expired, or was deleted. |
| 429 | `{"error":"rate_limited", ...}` or `{"error":"usage_exceeded", ...}` | Too many requests; honour `Retry-After`. |
| 402 | tool result with `PLAN_UPGRADE_REQUIRED` | The workspace's plan does not include MCP access. |
Errors at the credential layer use the RFC 6749 shape (`error`, `error_description`). Errors inside a tool call come back as MCP tool results; see [Errors](/reference/errors).
---
# ChatGPT
> Add IndexZero to ChatGPT as a connector so it can call the tools in a conversation.
Source: https://docs.indexzero.site/agents/chatgpt
ChatGPT connects to remote MCP servers through **connectors**. Custom connectors are available on plans that allow them and may need developer mode enabled in settings; the steps below assume it is.
### Open connector settings [#open-connector-settings]
In ChatGPT, open **Settings**, then **Connectors**. Choose **Create** (or **Add custom connector**).
### Enter the endpoint [#enter-the-endpoint]
```
https://app.indexzero.site/mcp
```
Name it "IndexZero". Choose OAuth as the authentication method; no client id or secret is required, because the server registers clients dynamically.
### Approve [#approve]
ChatGPT opens app.indexzero.site. Sign in if needed and approve the connection.
### Use it in a chat [#use-it-in-a-chat]
Enable the IndexZero connector for a conversation from the tools menu and ask it to list your projects.
Use the tool catalog with function calling instead. `https://app.indexzero.site/api/mcp/catalog` returns every tool in the shape function-calling APIs expect, and requests to the endpoint can be authenticated with an [API key](/agents/api-keys). See [Any other client](/agents/other-clients).
---
# Claude Code
> Add IndexZero to Claude Code with one command and approve it in the browser.
Source: https://docs.indexzero.site/agents/claude-code
Claude Code supports remote MCP servers over Streamable HTTP with OAuth, so it needs only the endpoint URL.
### Add the server [#add-the-server]
```bash
claude mcp add --transport http indexzero https://app.indexzero.site/mcp
```
Add `--scope user` to make it available in every project on the machine rather than just the current directory, or `--scope project` to check the configuration into the repository's `.mcp.json` for the rest of your team.
### Approve the connection [#approve-the-connection]
The first time Claude Code talks to the server, it opens a browser window on app.indexzero.site. Sign in if you are not already, then approve the connection. The consent screen names the client and the scopes it asked for (`mcp`, and `offline_access` if it wants a refresh token).
### Confirm it works [#confirm-it-works]
In a Claude Code session, ask:
> List my IndexZero projects.
You should get the projects you see in the dashboard. Run `/mcp` inside Claude Code to see the server's status and re-authenticate if needed.
## Checking in the configuration [#checking-in-the-configuration]
For a project-scoped setup, `.mcp.json` at the repository root looks like this:
```json title=".mcp.json"
{
"mcpServers": {
"indexzero": {
"type": "http",
"url": "https://app.indexzero.site/mcp"
}
}
}
```
Each teammate goes through their own OAuth approval and connects their own workspace; the file holds no credentials.
## Using an API key instead [#using-an-api-key-instead]
If the machine cannot open a browser (a CI runner, a remote box), create an [API key](/agents/api-keys) and pass it as a header:
```bash
claude mcp add --transport http indexzero https://app.indexzero.site/mcp \
--header "x-api-key: iz_your_key_here"
```
## Removing the server [#removing-the-server]
```bash
claude mcp remove indexzero
```
Revoking access on the IndexZero side is done from the app: delete the API key, or, for OAuth, disconnect the client from the AI & MCP page.
---
# Claude desktop and web
> Add IndexZero as a custom connector in Claude's settings.
Source: https://docs.indexzero.site/agents/claude
Claude's desktop app and claude.ai connect to remote MCP servers through **Connectors**. IndexZero supports the OAuth flow they expect, so you paste one URL and approve in the browser.
### Open connector settings [#open-connector-settings]
In Claude, open **Settings**, then **Connectors**, then **Add custom connector**.
### Paste the endpoint [#paste-the-endpoint]
```
https://app.indexzero.site/mcp
```
Give it a name such as "IndexZero". No client id or secret is needed: the server supports dynamic client registration.
### Approve [#approve]
Claude opens app.indexzero.site. Sign in if needed and approve the connection. The consent screen lists the scopes requested.
### Enable it in a conversation [#enable-it-in-a-conversation]
Connectors can be toggled per conversation from the tools menu in the composer. Turn IndexZero on and ask Claude to list your projects.
Connectors are tied to your Claude account, and the IndexZero workspace they reach is the one you approved with. Approving again from a different IndexZero account switches which workspace the connector uses.
## Working in Claude [#working-in-claude]
A few patterns that work well once connected:
* Start with a project. "Which of my IndexZero projects is for acme.com?" resolves the `projectId` every other tool needs.
* Ask for a plan before spending. "Estimate what a rank check on the acme tracker would cost" is free; Claude will call `estimate_rank_tracker_cost` and report the number.
* Prefer measured data for your own site. "What are my striking-distance queries in Search Console?" is free and more accurate than a paid estimate.
---
# Codex
> Register IndexZero with the Codex CLI.
Source: https://docs.indexzero.site/agents/codex
Codex supports remote MCP servers over Streamable HTTP and manages them with `codex mcp`.
### Add the server [#add-the-server]
```bash
codex mcp add indexzero --url https://app.indexzero.site/mcp
```
This writes the server into Codex's `config.toml`. The equivalent by hand:
```toml title="~/.codex/config.toml"
[mcp_servers.indexzero]
url = "https://app.indexzero.site/mcp"
```
### Authenticate [#authenticate]
```bash
codex mcp login indexzero
```
A browser window opens on app.indexzero.site; approve the connection. If your Codex build does not offer a login command, it will prompt on first use.
### Confirm [#confirm]
```bash
codex mcp list
```
IndexZero should show as connected. In a session, ask Codex to list your IndexZero projects.
## Using an API key instead [#using-an-api-key-instead]
For a non-interactive setup, create an [API key](/agents/api-keys) and pass it as a header:
```toml title="~/.codex/config.toml"
[mcp_servers.indexzero]
url = "https://app.indexzero.site/mcp"
http_headers = { "x-api-key" = "iz_your_key_here" }
```
Codex versions differ in the exact header option name; `codex mcp add --help` shows the one your build accepts.
---
# Cursor
> Add IndexZero to Cursor with a four-line mcp.json.
Source: https://docs.indexzero.site/agents/cursor
Cursor reads MCP servers from `mcp.json`, either globally at `~/.cursor/mcp.json` or per project at `.cursor/mcp.json`. Remote servers with OAuth need only the URL.
### Add the server [#add-the-server]
```json title="~/.cursor/mcp.json"
{
"mcpServers": {
"indexzero": {
"url": "https://app.indexzero.site/mcp"
}
}
}
```
Use `.cursor/mcp.json` inside a repository instead if the setup should travel with the project. The file holds no credentials either way.
### Reload and approve [#reload-and-approve]
Reload Cursor. In **Settings**, **MCP**, the IndexZero server appears with a prompt to authenticate. Approve the connection in the browser window that opens on app.indexzero.site.
### Confirm [#confirm]
Open the agent panel and ask it to list your IndexZero projects. The server's tools appear in the MCP settings once connected.
## Using an API key instead [#using-an-api-key-instead]
If OAuth is not an option in your environment, create an [API key](/agents/api-keys) and send it as a header:
```json title="~/.cursor/mcp.json"
{
"mcpServers": {
"indexzero": {
"url": "https://app.indexzero.site/mcp",
"headers": {
"x-api-key": "iz_your_key_here"
}
}
}
}
```
Do not commit a file containing a key. Keep the header form in the global `~/.cursor/mcp.json` and the URL-only form in the project file.
---
# Connecting an agent
> One MCP endpoint, two ways to authenticate. Which to use, what the agent gets, and what it is told about cost.
Source: https://docs.indexzero.site/agents
IndexZero runs a [Model Context Protocol](https://modelcontextprotocol.io) server at:
```
https://app.indexzero.site/mcp
```
It speaks **Streamable HTTP** and exposes tools: the same ones the dashboard uses, spending the same credits, scoped to the same projects. Legacy SSE transport is not supported.
Connecting an agent requires the Starter plan or above. On the Free plan the endpoint authenticates but every tool call is refused with a plan-upgrade error. See [Plans](/plans).
## Pick your client [#pick-your-client]
## Two ways to authenticate [#two-ways-to-authenticate]
**OAuth** is for interactive clients. You give the client the endpoint URL and nothing else; it discovers the authorization server, registers itself, and opens a browser window for you to approve. Tokens are scoped, expire, and can be refreshed. Details in [OAuth](/agents/oauth).
**API keys** are for everything without a browser: scripts, CI, a server-side agent, a client that does not implement OAuth. Create one on the **AI & MCP** page in the app and send it as an `x-api-key` header. Keys are confined to the MCP endpoint and can never reach account or billing endpoints. Details in [API keys](/agents/api-keys).
Both arrive at the same server with the same tools. The only difference is how the request proves which workspace it belongs to.
## What the agent is told [#what-the-agent-is-told]
The server hands every connected client a set of instructions along with the tool list. In short:
* Most tools read live search data and spend the workspace's credits; call `whoami` first to see the plan and the remaining balance.
* Do normal focused research without asking, but confirm with the user before any single action expected to cost more than about 2,000 credits. `run_rank_tracker` on a large tracker and `run_site_audit` on a big site are the two that get expensive.
* `estimate_rank_tracker_cost` is free; always call it before `run_rank_tracker` and report the number.
* `run_site_audit` is asynchronous; poll `get_audit_status` until it reports `completed` before reading issues.
* Search Console, Analytics, and PostHog tools are free and read the user's own measured data; prefer them over paid estimates when the question is about the user's own site.
Every tool's description also says whether it costs credits, so a well-behaved agent can budget without reading this page.
## What a tool call returns [#what-a-tool-call-returns]
Each result carries the full rows as a text table (not a summary), the same data as structured content, and a small `_meta` block with the organization and project ids, a deep link into the dashboard where relevant, and, for paid calls, the credits charged and remaining. Errors come back as tool results marked `isError` with an actionable hint, never as a dropped connection. See [Errors](/reference/errors).
## Verifying the connection [#verifying-the-connection]
Ask the agent to list your IndexZero projects. If it answers with the projects you see in the dashboard, the connection, the plan gate, and the workspace binding are all correct. `whoami` is the equivalent single call.
---
# OAuth
> How interactive clients authorize, what the scopes mean, token lifetimes, and the discovery endpoints.
Source: https://docs.indexzero.site/agents/oauth
Interactive MCP clients authenticate with **OAuth 2.1**: authorization code with PKCE (`S256`), plus **dynamic client registration** (RFC 7591) so a client needs no pre-provisioned credentials. You give the client the endpoint URL; the rest is discovery.
## The flow [#the-flow]
1. The client requests `https://app.indexzero.site/mcp` and receives a 401 with protected-resource metadata pointing at the authorization server.
2. It reads the authorization server metadata, registers itself, and sends you to the authorization endpoint in a browser.
3. You sign in to IndexZero if needed and see a consent screen naming the client and the scopes it asked for. Approving binds the grant to the workspace you are signed in to.
4. The client exchanges the code for an access token (and a refresh token if it asked for `offline_access`) and starts calling tools.
## Scopes [#scopes]
| Scope | Meaning |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `mcp` | Call IndexZero MCP tools on behalf of the authorizing workspace. Required on every grant; a token without it authenticates but reaches no tools. |
| `offline_access` | Issue a refresh token so a long-running agent keeps working after the access token expires. |
A request whose token lacks `mcp` receives HTTP 403 with an `insufficient_scope` error and a hint to re-authorize.
## Token lifetimes [#token-lifetimes]
| Token | Lifetime |
| --------------------------- | -------- |
| Access token | 24 hours |
| Refresh token | 30 days |
| Dynamic client registration | 365 days |
A client that requested `offline_access` refreshes silently. One that did not will prompt you to approve again after 24 hours.
## Discovery endpoints [#discovery-endpoints]
All on the product origin, because the issuer and the resource live there:
| Document | URL |
| ---------------------------------------- | ------------------------------------------------------------------- |
| Authorization server metadata (RFC 8414) | `https://app.indexzero.site/.well-known/oauth-authorization-server` |
| Protected resource metadata (RFC 9728) | `https://app.indexzero.site/.well-known/oauth-protected-resource` |
| Authorization endpoint | `https://app.indexzero.site/api/auth/oauth2/authorize` |
| Token endpoint | `https://app.indexzero.site/api/auth/oauth2/token` |
| Registration endpoint | `https://app.indexzero.site/api/auth/oauth2/register` |
The marketing origin, indexzero.site, redirects the two well-known lookups here rather than serving copies, so a client that starts from the wrong hostname still validates against the canonical document.
## Which workspace a grant reaches [#which-workspace-a-grant-reaches]
A grant is bound to the workspace you were signed in to when you approved. Each person on a team approves separately and reaches their own workspace; a shared configuration file (a repository's `.mcp.json`, say) carries no credentials.
## Revoking [#revoking]
Disconnect the client from the **AI & MCP** page in the app. Tokens stop working immediately; the client will prompt to authorize again the next time it is used.
---
# Any other client
> The generic configuration for Hermes, OpenClaw, and anything else that speaks MCP, plus calling the endpoint directly.
Source: https://docs.indexzero.site/agents/other-clients
Any client that supports remote MCP servers over **Streamable HTTP** can use IndexZero. You need exactly one fact, the endpoint:
```
https://app.indexzero.site/mcp
```
If the client supports OAuth for remote servers, that is all: it discovers the authorization server from the endpoint, registers itself, and opens a browser for approval. If it does not, create an [API key](/agents/api-keys) and send it as an `x-api-key` header.
## The common configuration shape [#the-common-configuration-shape]
Most clients, including Hermes and OpenClaw, accept the same JSON shape Cursor and Claude Code use:
```json
{
"mcpServers": {
"indexzero": {
"url": "https://app.indexzero.site/mcp"
}
}
}
```
With an API key:
```json
{
"mcpServers": {
"indexzero": {
"url": "https://app.indexzero.site/mcp",
"headers": {
"x-api-key": "iz_your_key_here"
}
}
}
}
```
Some clients call the transport `"type": "http"` or `"transport": "streamable-http"`; use whichever your client documents. Do not choose SSE: the server rejects the legacy SSE transport.
## Calling the endpoint directly [#calling-the-endpoint-directly]
The endpoint is plain JSON-RPC 2.0 over HTTP. This lists the tools with an API key:
```bash
curl -X POST https://app.indexzero.site/mcp \
-H "x-api-key: iz_your_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/list" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}'
```
The full header set matters. The current protocol revision requires `MCP-Protocol-Version`, the `Mcp-Method` mirror of the body (and `Mcp-Name` for `tools/call`), and the `_meta` envelope; a shorter request fails with a `-32020` error. An MCP SDK sends these for you, which is the recommended route for anything beyond a smoke test.
A tool call looks like this:
```bash
curl -X POST https://app.indexzero.site/mcp \
-H "x-api-key: iz_your_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/call" \
-H "Mcp-Name: whoami" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "whoami",
"arguments": {},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}'
```
## Function calling without MCP [#function-calling-without-mcp]
If your stack uses an LLM provider's native function calling rather than MCP, fetch the catalog:
```
GET https://app.indexzero.site/api/mcp/catalog
```
It is unauthenticated and returns every tool with a name, description, and JSON Schema for its arguments, in the shape function-calling APIs expect. Hand the definitions to your model, and when it picks one, execute it with a `tools/call` request as above. See [Machine-readable reference](/reference/machine-readable).
## CORS [#cors]
The endpoint answers preflight requests and allows any origin, with the headers `Content-Type`, `Accept`, `Authorization`, `mcp-session-id`, `MCP-Protocol-Version`, `Mcp-Method`, and `Mcp-Name`, and exposes `mcp-session-id`. Browser-based clients therefore work, but keep API keys out of browser code: use OAuth there.
---
# How credits work
> Credits pay for live search data. Roughly 1,000 credits is one dollar of provider spend. Anything that reads your own data is free.
Source: https://docs.indexzero.site/credits
Credits are the unit every paid call is priced in. They exist so the product can buy live search data from its provider on your behalf without you holding a provider account, and so an agent can be told the price of an action before taking it.
## What one credit is [#what-one-credit-is]
Every paid call has a provider cost in dollars. IndexZero charges:
```
credits = ceil(providerCostUsd × 1.28 × 1000)
```
That is, **1,000 credits is roughly $1 of provider spend**, with a 28% margin, rounded up per call. Rounding happens per metered call, not on the monthly total, so a call that costs $0.0002 still costs one credit.
## What is free [#what-is-free]
Anything that reads data you already own or data IndexZero has already bought is free:
* **Search Console, Google Analytics 4, and PostHog** reports and the search-opportunity joins across them.
* **Saved results**: saved keywords and their cached metrics, rank-tracker snapshots and history, audit status, issues, and pages.
* **Workspace calls**: `whoami`, listing and creating projects, creating and editing rank trackers, and estimating a rank check.
Everything that buys live search data costs credits: keyword research and metrics, domain and backlink analysis, local search, running a rank check, running a site audit, and the AI-visibility tools. Each tool's [reference page](/tools) says which, and the MCP tool descriptions say so too, so an agent knows before it calls.
## Two balances [#two-balances]
Your workspace has two balances, and they behave differently.
| | Monthly allowance | Top-up credits |
| ------------------- | ------------------------------------------------------ | --------------------------------------- |
| Where it comes from | Your plan, granted at the start of each billing period | One-time purchases |
| Resets | On your renewal date. Unused credits do not roll over. | Never. Top-ups do not expire. |
| Spent | First | Only once the monthly allowance is gone |
Spending always draws from the monthly allowance first. That order is deliberate: a top-up bought mid-cycle is never wiped by the reset, because it is untouched until the allowance runs out.
`whoami` reports both balances and the period end, and the billing page in the app shows every charge with the tool that made it.
## Before every paid call [#before-every-paid-call]
Every paid call is checked against your balance before it runs and recorded after. The check is simple: if the balance is already zero, the call is refused with HTTP 402 and an `INSUFFICIENT_CREDITS` error, and nothing is charged. A call that starts with a positive balance runs to completion and can bring the balance to zero, but the balance never goes negative.
Two calls can cost a lot at once, and both give you a way to bound them:
* **Rank checks.** `estimate_rank_tracker_cost` is free and returns the exact credit cost of a run. `run_rank_tracker` accepts `maxCostCredits` and refuses, at no cost, if the estimate exceeds it. The [rank-tracking page](/tools/rank-tracking) has the full cost model with worked examples.
* **Site audits.** Cost scales with page count. `maxPages` is capped by your plan rather than rejected, so the most a run can cost is bounded by the cap. See [site audits](/tools/site-audits).
The MCP server also tells every connected agent, in its instructions, to call `whoami` first, to always estimate before running a rank check, and to confirm with you before any single action expected to cost more than about 2,000 credits.
## Running low [#running-low]
The app warns when the remaining balance drops below 10% of the plan's monthly allowance, with a floor of 250 credits so the warning on the Free plan still arrives while there is enough left to do something.
When the balance is exhausted, the free tools keep working. Paid calls return 402 until the next renewal or a top-up. Top-up packs are on the [plans page](/plans).
## Scheduled work and credits [#scheduled-work-and-credits]
A scheduled rank check that finds the workspace without enough credits is skipped, not run into debt, and the tracker records the reason (`insufficient_credits`) so you can see why a data point is missing. The same applies if the workspace drops to a plan without scheduled tracking (`plan_required`).
---
# Getting started
> Create an account, point a project at your domain, run your first research, and connect an agent.
Source: https://docs.indexzero.site/getting-started
### Create an account [#create-an-account]
Sign up at [app.indexzero.site/sign-up](https://app.indexzero.site/sign-up). There is no sales call and no card required: a new workspace starts on the Free plan with credits a month and the full dashboard.
### Create a project [#create-a-project]
Everything in IndexZero is scoped to a project. A project pins two things:
* **A domain.** This is what gets audited, what rank tracking defaults to, and what backlink and domain tools analyse when you do not name another domain.
* **A market**, meaning a location and a language (United States and English by default). Every research tool uses it when you do not pass one explicitly, so a project for a German site set to Germany and `de` gets German volumes without you saying so on every call.
The Free plan allows active project; see [Plans](/plans) for the others. Archiving a project frees its slot.
### Run your first research [#run-your-first-research]
Open the project and try **Keywords**: enter one to five seed terms and you get back hundreds of related keywords with volume, difficulty, CPC, and intent. That call spends credits; the [credits page](/credits) explains how many and why. Reading your own Search Console or Analytics data is free once connected.
### Connect an agent [#connect-an-agent]
MCP access starts on the Starter plan. Once you are on it, open **AI & MCP** in the app, copy the endpoint, and follow the guide for your client. The one-liner for Claude Code is:
```bash
claude mcp add --transport http indexzero https://app.indexzero.site/mcp
```
A browser window opens for you to approve the connection. Ask the agent to list your IndexZero projects to confirm it worked. Guides for every client are under [Connect an agent](/agents).
## What to read next [#what-to-read-next]
---
# Account, billing, and data
> Signing in, the billing page, what is kept, and how to delete things.
Source: https://docs.indexzero.site/guides/account
## Signing in [#signing-in]
Sign in at [app.indexzero.site/login](https://app.indexzero.site/login) with email and password, or with Google. Sign-up is email and password with a verification link; you can use Google from the sign-in page afterwards. A password reset signs out every session and sends a confirmation email.
One account is one workspace. There are no teams, invitations, or roles; billing, credits, plans, and limits are all per workspace.
## Billing [#billing]
**Settings, Billing** shows the plan, both credit balances, the renewal date, a usage chart, and every transaction with the feature that spent it (keyword research, domain overview, backlinks, site audit, rank tracking, AI citations, AI prompt responses, local SEO, SERP, agent). Upgrades, downgrades, and top-up packs are bought here; payments are handled by Dodo Payments as merchant of record, so card details never reach IndexZero. A cancelled subscription keeps its plan until the end of the paid period.
When credits run low the app shows a banner and emails the workspace owner once per period; when they run out, paid research and tracking pause until a top-up or renewal, and everything already collected stays. See [How credits work](/credits).
## Deleting things [#deleting-things]
Projects are archived rather than deleted and can be restored. Keywords, tags, trackers, audits, SAM sessions, and API keys can be deleted individually from the product. Disconnecting Google Search Console or Analytics deletes the stored tokens; disconnecting PostHog deletes the stored key.
## Closing your account [#closing-your-account]
Email [support@indexzero.site](mailto:support@indexzero.site) from the address on the account. Verified requests are actioned within 30 days: the account, its workspace, projects, keywords, audits, rank data, ledger, and keys are removed. Records that must be kept for legal, tax, or accounting reasons are retained for as long as those obligations require. The [privacy policy](https://indexzero.site/privacy-policy) has the full statement, including the sub-processors involved.
---
# AI visibility in the dashboard
> Brand lookup and the prompt explorer, on Starter and above.
Source: https://docs.indexzero.site/guides/ai-visibility
AI visibility measures whether assistants mention and cite you. It has two screens in the project sidebar under **AI Search**, both available on Starter and above. The agent tools are documented under [AI visibility](/tools/ai-visibility).
## AI Visibility (brand lookup) [#ai-visibility-brand-lookup]
Enter your brand, product, or domain and up to nine competitors. The result shows how often each is mentioned by ChatGPT and in Google's AI Overviews, share of voice across the set, and which sources those answers cite, which is the list of pages you would want to be on. ChatGPT data covers the United States and English only.
## Prompt Explorer [#prompt-explorer]
Ask one question across ChatGPT, Claude, Gemini, and Perplexity and compare the answers and citations side by side, with a brand highlighted wherever it appears. Web search is on by default so the answers reflect what a real user would see, and can be localised to a country. Each model selected costs credits; responses are cached for a week, so re-running a prompt within that window is free.
---
# Connecting Google and PostHog
> Read-only access to Search Console, Google Analytics 4, and PostHog, connected per project. All reads are free.
Source: https://docs.indexzero.site/guides/integrations
Three integrations give IndexZero your measured data. Reading it is always free, and the agent tools that use it are documented under [Search Console and Google Analytics](/tools/search-console-and-analytics) and [PostHog](/tools/posthog).
## Google Search Console [#google-search-console]
### Connect [#connect]
Open **Search Console** in the project sidebar and choose Connect. Google asks for read-only Search Console access (`webmasters.readonly`), plus your basic profile so the connection can be labelled.
This is a separate consent from signing in with Google. Signing in never carries these scopes, and revoking the integration never signs you out.
### Pick a property [#pick-a-property]
The page lists the properties the Google account can see. Choose the one for this project's site. The connection is recorded only once a property is selected.
What you get: clicks, impressions, CTR, and average position by query, page, country, device, date, and search appearance, for web, image, video, news, and Discover; striking-distance queries (positions 5 to 20); and URL inspection for up to 10 URLs at a time. Data lags Google by about three days.
## Google Analytics 4 [#google-analytics-4]
The same flow, from **Analytics** in the project sidebar, asking for read-only Analytics access (`analytics.readonly`). Pick the GA4 property for the site.
What you get: landing pages, page performance, key events, traffic acquisition, ecommerce performance, site search, and audience breakdown, filtered to organic search or all channels; a measurement-health check; and the search-opportunities report that joins Search Console with Analytics to find pages that rank but do not convert.
## How Google data is handled [#how-google-data-is-handled]
IndexZero's use of information received from Google APIs adheres to the Google API Services User Data Policy, including the Limited Use requirements. Google data is never sold, never used for advertising, never sent to AI model providers for training, and only read by a person with your explicit consent or for security and legal reasons. Refresh tokens are encrypted at rest. Disconnecting from the integration page deletes the stored tokens; you can also revoke access at [myaccount.google.com/permissions](https://myaccount.google.com/permissions). The full statement is in the [privacy policy](https://indexzero.site/privacy-policy).
## PostHog [#posthog]
PostHog has no third-party OAuth for reading data, so the connection uses a personal API key.
### Create a scoped personal API key [#create-a-scoped-personal-api-key]
In PostHog, create a **personal API key** (it starts with `phx_`). Scope it to a single project with two permissions: `query:read` and `project:read`. The first runs the reports; the second lets IndexZero list your projects so you can pick one from a menu.
Do not paste a project API key (`phc_`). That is the write-only ingest token already in your website's JavaScript, and IndexZero rejects it up front rather than failing later.
### Connect [#connect-1]
At the bottom of the **Analytics** page, choose your region, PostHog Cloud US or EU, or enter a custom host for a self-hosted instance. Paste the key and pick the project.
The key is sent in the request body, never a query string, and is encrypted at rest. Disconnecting deletes it.
What you get: sessions, visitors, pageviews, bounce rate, and session duration with a daily trend; entry, top, and exit pages; traffic acquisition by channel, referrer, or UTM; events; conversions for an event you name; audience by device, country, browser, or OS; and the search-opportunities join against Search Console. Organic filtering uses PostHog's own "Organic Search" channel type.
---
# Projects and markets
> What a project holds, how its market drives every tool, and how archiving works.
Source: https://docs.indexzero.site/guides/projects
A project is the unit everything else hangs off: research, trackers, audits, integrations, and credit attribution are all per project. Your workspace starts with one project named "Default".
## What a project holds [#what-a-project-holds]
| Field | Notes |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Name | 1 to 80 characters. |
| Domain | Optional at creation, but required before audits, rank tracking, and own-site backlink tools will work. Entered as a bare domain; a pasted URL is reduced to its hostname. |
| Market | A location and a language. United States and English by default. |
Name and domain are edited on the project's **Settings** page. The market is chosen when the project is created and can be overridden per call.
## How the market works [#how-the-market-works]
Every research tool takes an optional `locationCode` and `languageCode`. When you leave them out, the project's market is used, so a project set to Germany and `de` gets German volumes and German SERPs without repeating that on every call. Pass them explicitly to look at a different country for one query.
Two things to know about markets:
* **Location and language must be a valid pair.** The dashboard's market picker snaps the language to the chosen country's default because the provider rejects a mismatched pair as a failed, charged task.
* **Some countries have less data.** Keyword difficulty and search intent come from a dataset that covers 94 countries. Elsewhere, keyword data comes from Google Ads and has volume and CPC but no difficulty or intent, and clickstream refinement has no effect.
## Limits [#limits]
Active (non-archived) projects are capped per plan: on Free, on Starter, on Pro, on Scale. Creating one past the cap fails with a plan-limit error that names the cap.
## Archiving [#archiving]
Projects are archived, not deleted. Archiving frees the slot; the project's data stays and it can be restored from the **Archived** tab on the projects page. You cannot archive your last active project.
## In the dashboard [#in-the-dashboard]
Each project has its own sidebar: Dashboard; Research (Keyword Research, Domain Overview, Backlinks, Local SEO); My Site (Rank Tracking, Saved Keywords, Site Audit); AI Search (SAM, AI Visibility, Prompt Explorer); Connect (Search Console, Analytics, AI & MCP); and Project Settings. The dashboard page shows an activation checklist until you have added a site, researched keywords, created a tracker, run an audit, connected Search Console, and connected an agent.
---
# Rank tracking in the dashboard
> Create a tracker, understand the cost line, choose a schedule, and read the results.
Source: https://docs.indexzero.site/guides/rank-tracking
Rank tracking records where a domain ranks for a set of keywords each time a check runs. The agent tools are documented under [Rank tracking](/tools/rank-tracking), including the [cost model](/tools/rank-tracking#the-cost-model). This page is the dashboard view of the same thing.
## Creating a tracker [#creating-a-tracker]
Open **Rank Tracking** in the project sidebar and choose New tracker. The two-step dialog asks for:
* **Keywords**, up to 1,000, one per line.
* **Market**: the country and language, from the same picker as research. Optionally a **City** in canonical form (`Pittsburgh,Pennsylvania,United States`), which switches the tracker to city-level local results. For local trackers, keyword volumes come from Google Ads at that city, the only source with city-level numbers.
* **Devices**: Desktop + Mobile (the default, two checks per keyword), desktop, or mobile.
* **Depth**: Top 10, 20 (default), 30, 50, or 100.
* **Schedule**: daily, weekly (default), monthly, or manual.
Before you confirm, the dialog shows the cost of one check and, for a schedule, the projected monthly cost: daily counts 30 checks a month, weekly 4, monthly 1. Scheduled checks use the provider's task queue and cost about 30% of a manual check.
Manual trackers with a Check now button work on every plan, including Free. Daily, weekly, and monthly schedules require Starter or above. Trackers are counted workspace-wide: on Free, on Starter, on Pro, on Scale.
## Checking now [#checking-now]
**Check now** on a tracker's page shows the exact credit cost and asks you to approve it. The approved figure is a ceiling: the run re-estimates before spending and aborts if the real cost would exceed it. Only one check per tracker runs at a time.
## How schedules run [#how-schedules-run]
A new schedule is given a random time between 04:00 and 09:00 UTC so checks spread across the day. Later runs advance from the previous scheduled time, so a delayed run does not drift the schedule; monthly checks land on the last day of the month. A scheduled check is skipped, and the reason recorded on the tracker, when the workspace is out of credits, the plan no longer includes scheduling, or the tracker has no keywords.
## Reading results [#reading-results]
A tracker's page shows the keyword table with the latest position per device, a position matrix across runs, a trend chart, per-keyword history, and the list of runs. A blank position means the domain was not found within the tracker's depth. Removing a keyword keeps its history, so re-adding it later loses nothing.
---
# SAM
> The SEO teammate built into the app, on Pro and above.
Source: https://docs.indexzero.site/guides/sam
SAM is an in-app chat agent that works on one project at a time with the same tools an external agent gets over MCP, plus the ability to read your site. It is available on Pro and Scale from **SAM** in the project sidebar.
## What SAM can do [#what-sam-can-do]
* Call every MCP tool, executed in-process, so SAM and an external agent get identical behaviour, gating, and cost for the same tool.
* Read pages on the web: list the internal links on a page, and fetch the visible text of up to five pages at a time. Both are free and go through the same safety policy as the crawler.
* Remember. SAM keeps a durable note of project facts and a log of research it has already done, both injected into every conversation, so it does not buy the same data twice.
## How SAM spends [#how-sam-spends]
SAM follows the same rules as a connected agent: focused research without asking, confirmation before any single action likely to cost more than about 2,000 credits, a free estimate before every rank check, and a preference for your measured Search Console and Analytics data over paid estimates. A turn is capped at 24 tool calls so a confused turn cannot spend without bound. When the workspace is out of credits, SAM stops rather than retrying.
SAM's own model usage is metered too, charged after each answer at the provider's reported cost and shown under the "agent" bucket on the billing page.
## Sessions [#sessions]
Each conversation is a session; the sidebar lists the 50 most recent. You can rewind to an earlier message and resend, which discards everything after it, and delete a session, which destroys its transcript.
---
# Site audits in the dashboard
> Launch a crawl, watch it run, and work through the issues.
Source: https://docs.indexzero.site/guides/site-audits
A site audit crawls the project's domain and checks every page against issue types, with Lighthouse on a sample. The crawler's behaviour, the page caps, and the agent tools are documented under [Site audits](/tools/site-audits); the checks themselves are listed under [Audit issue types](/reference/audit-issue-types).
## Launching [#launching]
Open **Site Audit** in the project sidebar. The launch form asks for a page budget (default 50) and whether to run Lighthouse (on by default). The budget is capped by plan: pages on Free, on Starter, on Pro, on Scale. Asking for more is trimmed to the cap, and the page says so, rather than refused. The project needs a domain.
Cost scales with the number of pages crawled, plus up to 20 Lighthouse runs (10 URLs, mobile and desktop).
## While it runs [#while-it-runs]
The audit page shows the phase (discovery, crawling, lighthouse, finalizing), pages crawled so far, and a live feed of URLs. Crawls take minutes; a large site with a slow server takes longer. If the site takes too long to respond the audit fails with a message suggesting a smaller page limit.
Bot protection that challenges the crawler is reported honestly as a blocked page, not a broken one. Allowlist the `IndexZero-Audit` user agent in your WAF (on Cloudflare, a WAF custom rule that skips bot protection when the user agent contains that string) and run the audit again.
## Reading results [#reading-results]
The finished audit shows issue counts by severity, then each issue type as an expandable group with the explanation, the fix, and the affected URLs. Work through critical issues first. Below that are the crawled pages with status, title, word count, and H1s, and the Lighthouse table with scores and Core Web Vitals per sampled URL. Everything exports to CSV.
Deleting an audit removes its results and its crawl state.
---
# IndexZero documentation
> IndexZero is the SEO platform built for AI agents. Keyword research, rank tracking, site audits, backlinks, and AI-visibility measurement, callable over MCP by Claude, ChatGPT, Cursor, or anything you build, and by you in the dashboard.
Source: https://docs.indexzero.site/
IndexZero gives an AI agent a real SEO stack. Every tool the dashboard has is also an MCP tool on one endpoint, spending the same credits, scoped to the same projects. You point your agent at it once; after that it runs the research and you read the results.
## What IndexZero does [#what-indexzero-does]
| Area | What you get |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Keyword research | Seeds become hundreds of scored keywords with volume, difficulty, CPC, competition, and intent, for any country and language. |
| Domain and backlink research | Estimated traffic, ranked keywords, top pages, SERP competitors, and the link profile of any domain, yours or a competitor's. |
| Local search | Google Business listings, Maps and Local Finder rankings, and the questions people ask on a listing, at a coordinate. |
| Rank tracking | Scheduled or on-demand position checks, national or city-level, desktop and mobile, with a cost estimate before every run. |
| Site audits | A robots-aware crawler over your own site checking issue types, with Lighthouse on a sample of pages. |
| Your own measured data | Search Console, Google Analytics 4, and PostHog reports for a connected project. Free, because it is your data. |
| AI visibility | Whether ChatGPT and Google's AI Overviews mention your brand, your share of voice against competitors, and what the assistants cite instead. |
## Two ways in [#two-ways-in]
**The dashboard** at [app.indexzero.site](https://app.indexzero.site) is where you create projects, connect Google and PostHog, manage billing, and read results. Everything an agent does shows up there.
**The MCP server** at `https://app.indexzero.site/mcp` exposes tools over Streamable HTTP. Interactive clients authenticate with OAuth; scripts use an API key. See [Connect an agent](/agents).
## Reading these docs as an agent [#reading-these-docs-as-an-agent]
Every page here is served as markdown too. Append `.md` to any URL (for example [`/credits.md`](/credits.md)), or request the page with `Accept: text/markdown`. [`/llms.txt`](/llms.txt) indexes every page and [`/llms-full.txt`](/llms-full.txt) is the whole manual in one file.
The product-level machine-readable surface, including the OpenAPI document and the full tool catalog with JSON Schema, is described in [Machine-readable reference](/reference/machine-readable).
---
# Plans and limits
> What each plan includes, the limits it enforces, and the top-up packs.
Source: https://docs.indexzero.site/plans
Plans set the monthly credit allowance and the limits below. Every limit is enforced by the server and read from the same catalog this table renders from, so the number here is the number a call is checked against.
Prices are in USD per month. Sign-up is self-serve at [app.indexzero.site/sign-up](https://app.indexzero.site/sign-up); upgrades and downgrades happen on the billing page in the app.
## What the limits mean [#what-the-limits-mean]
* **Credits per month** is the allowance granted at the start of each billing period. It does not roll over. See [How credits work](/credits).
* **Active projects** counts projects that are not archived. Creating one past the cap fails with an upgrade message; archiving frees the slot.
* **Pages per site audit** is a ceiling on `maxPages`. A request above it is trimmed to the cap rather than rejected, so you still get an audit.
* **Rank trackers** counts trackers across the whole workspace.
* **Scheduled rank checks** means daily, weekly, or monthly checks that run without you. Manual checks are available on every plan, including Free.
* **AI visibility** unlocks brand visibility measurement and the prompt explorer, in the dashboard and over MCP.
* **MCP server access** is required to create API keys or authorize an MCP client. The Free plan can use every tool in the dashboard but cannot connect an agent.
* **SAM agent** is the in-app SEO teammate, available on Pro and above.
## Top-up packs [#top-up-packs]
Top-ups are one-time purchases. They never expire and are spent only after the monthly allowance is exhausted.
## Plan gates in responses [#plan-gates-in-responses]
When a call needs a plan you are not on, the API returns HTTP 402 with a `PLAN_UPGRADE_REQUIRED` or `PLAN_LIMIT_REACHED` error that names the plan which unblocks it. The MCP server surfaces the same message to the agent, with a hint, so it can tell you what to do rather than retrying. See [Errors](/reference/errors).
---
# Audit issue types
> Every check a site audit runs, what each means, and how to fix it.
Source: https://docs.indexzero.site/reference/audit-issue-types
A site audit checks every crawled page against the issue types below, grouped by severity. The `issueType` argument of `get_audit_issues` accepts the id in the second line of each row. The explanations and fixes are the same text the dashboard shows.
## Critical [#critical]
## Warnings [#warnings]
## Informational [#informational]
## Thresholds used [#thresholds-used]
* **Slow response** means the HTML took more than 1.5 seconds.
* **Deep page** means five or more clicks from the homepage.
* **Title length** flags under about 10 characters or over about 60.
* **Meta description length** flags under about 70 characters or over about 160.
* **Blocked page** covers a bot challenge, a 401, 403, or 429, or a 503 whose body is a challenge page. The crawler identifies as `IndexZero-Audit/1.0`; allowlisting that user agent is the fix.
---
# Errors
> The error shapes on each layer, the codes that matter, and what an agent should do with each.
Source: https://docs.indexzero.site/reference/errors
There are three layers a request can fail on, and each has one shape.
## Inside a tool call [#inside-a-tool-call]
A tool that fails returns an MCP tool result marked `isError`, never a dropped connection or a protocol error. The text carries the code, the message, and where possible an actionable hint, so the agent can tell the user what to do rather than retrying.
| Code | Meaning | What to do |
| ------------------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `INSUFFICIENT_CREDITS` | The workspace is out of credits. | Stop paid calls. Free tools keep working. Top up or wait for renewal. |
| `PLAN_UPGRADE_REQUIRED` | The feature needs a higher plan; the message names it. | Do not retry. Tell the user which plan unblocks it. |
| `PLAN_LIMIT_REACHED` | A count limit was hit (projects, trackers). The message states the limit. | Archive or delete something, or upgrade. |
| `VALIDATION_ERROR` | An argument was missing or out of range. | Fix the arguments; the message says which. |
| `NOT_CONNECTED` | The project has no Search Console, Analytics, or PostHog connection. | Connect it in the dashboard. |
| `RECONNECT_REQUIRED` | A connection exists but its access has lapsed or been revoked. | Reconnect in the dashboard. |
| `PROVIDER_BILLING_ISSUE` | The upstream data provider refused the call. | Not something the user can fix; contact support if it persists. |
Not-found conditions use plain messages: `Project not found in this workspace.` (the same whether the id is unknown or belongs to someone else), `Tracker not found.`, `No audits found for this project.`
## In front of the endpoint [#in-front-of-the-endpoint]
Authentication failures at the credential layer use the RFC 6749 shape:
```json
{ "error": "invalid_api_key", "error_description": "The provided API key is invalid, expired, or disabled" }
```
| Status | `error` | Meaning |
| ------ | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| 401 | `invalid_api_key` | Unknown, expired, or deleted key. |
| 401 | `invalid_token` | Missing or expired OAuth token. The response carries `WWW-Authenticate` pointing at the protected-resource metadata. |
| 403 | `insufficient_scope` | The OAuth token lacks the `mcp` scope. Re-authorize. |
| 429 | `rate_limited` or `usage_exceeded` | Too many requests on an API key. Honour `Retry-After` (seconds). |
A JSON-RPC request that omits the required protocol headers or the `_meta` envelope fails with JSON-RPC error `-32020`. See [Any other client](/agents/other-clients) for the full header set.
## On HTTP endpoints [#on-http-endpoints]
Product HTTP endpoints return one JSON shape:
```json
{ "error": { "code": "PLAN_LIMIT_REACHED", "message": "Your plan includes 3 projects. Upgrade to add more.", "hint": "..." } }
```
| Status | Meaning |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400 | Validation or an impossible action, such as archiving the last active project (`CANNOT_ARCHIVE_LAST_PROJECT`). |
| 402 | Out of credits (`INSUFFICIENT_CREDITS`), or a plan gate (`PLAN_UPGRADE_REQUIRED`, `PLAN_LIMIT_REACHED`). Plan gates add a `denial` object with `feature`, `currentPlan`, `requiredPlan`, and for limits `{ name, allowed, current }`. |
| 502 | The data provider failed. |
| 503 | Payments are not configured on this deployment. |
## Rules of thumb for agents [#rules-of-thumb-for-agents]
* Never retry a 402 or a plan gate; the outcome will not change until the user acts.
* Treat `-32020` as a client bug, not a server outage.
* On 429, wait for `Retry-After` and continue; the limit is per minute.
* When a paid call fails after the balance check, nothing was charged.
---
# Limits
> Every numeric limit in one place. Plan limits, per-call maximums, rate limits, and lifetimes.
Source: https://docs.indexzero.site/reference/limits
## Plan limits [#plan-limits]
## Per-call maximums [#per-call-maximums]
| What | Limit |
| ------------------------------------------------------ | ----------------------------------------- |
| Seeds per `research_keywords` call | 5 |
| Rows from `research_keywords` | 500 (default 100) |
| Keywords per `get_keyword_metrics` call | 700 |
| Keywords per `save_keywords` call | 500 |
| Keywords per `find_serp_competitors` call | 200 |
| Rows from ranked keywords, top pages, backlink profile | 1,000 (default 100) |
| Competitors from `find_serp_competitors` | 200 (default 50) |
| Categories per `search_local_businesses` call | 10 |
| Local results per call | 100 (default 20) |
| Keywords per rank tracker | 1,000 |
| Keywords added or removed per call | 1,000 |
| Tracked keyword length | 200 characters |
| Pages per site audit | 10 to 10,000, capped by plan (default 50) |
| Issues or pages returned per audit call | 1,000 (defaults 200 and 100) |
| URLs per `inspect_urls` call | 10 |
| Rows from an Analytics or PostHog report | 500 (default 100) |
| Competitors per `get_brand_visibility` call | 9 |
| Prompt length for `run_ai_prompt` | 4,000 characters |
| Project name | 80 characters |
| API key name | 80 characters |
## Rate limits and lifetimes [#rate-limits-and-lifetimes]
| What | Value |
| ---------------------------------- | --------------------------------------------- |
| API key requests | 500 per minute per key |
| OAuth access token | 24 hours |
| OAuth refresh token | 30 days |
| Dynamic client registration | 365 days |
| Concurrent checks per rank tracker | 1 |
| Rank check watchdog | A run silent for 45 minutes is marked failed |
| Audit watchdog | An audit running for 2 hours is marked failed |
| Lighthouse sample | Up to 10 URLs, each on mobile and desktop |
| AI prompt response cache | 7 days |
| Search Console data lag | About 3 days |
## Credit thresholds [#credit-thresholds]
| What | Value |
| ------------------------------------ | --------------------------------------------------------------- |
| Credits per dollar of provider spend | 1,000, plus a 1.28 markup, rounded up per call |
| Low-credit warning | Below 10% of the monthly allowance, with a floor of 250 credits |
| Agent confirmation threshold | About 2,000 credits for a single action |
---
# Machine-readable reference
> Every fixed URL an agent can read to learn what IndexZero is and how to call it, without rendering a page.
Source: https://docs.indexzero.site/reference/machine-readable
Everything an agent needs to call IndexZero is published at a fixed URL. The documents are generated from the same constants the product enforces, so they cannot advertise a plan, a tool, or a scope the server does not have.
## Product-level documents [#product-level-documents]
| Document | URL | What it is |
| ------------ | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| llms.txt | `https://indexzero.site/llms.txt` | What IndexZero is for, when to use it and when not to, how to call it, cost and limits, and the tool groups. Per the llmstxt.org format. |
| OpenAPI 3.1 | `https://indexzero.site/openapi.json` | The MCP endpoint, the OAuth security schemes with named scopes, the API-key scheme, and the JSON error shape. |
| MCP manifest | `https://indexzero.site/.well-known/mcp.json` | Endpoint, transport, authentication schemes, scopes, tool groups, pricing, and links, in one JSON document. Also served from the app origin. |
| Tool catalog | `https://app.indexzero.site/api/mcp/catalog` | Every tool with name, description, and JSON Schema for its arguments, in the shape function-calling APIs expect. Unauthenticated. |
| Health | `https://app.indexzero.site/api/health` | `{ ok, service, env, database, timestamp }`. Unauthenticated. |
| Sitemap | `https://indexzero.site/sitemap.xml` | Every public marketing page. |
The discovery documents are cacheable for five minutes with an hour of stale-while-revalidate, so a deploy that moves an endpoint propagates quickly.
## OAuth discovery [#oauth-discovery]
On the app origin, because the issuer and the resource live there. The marketing origin redirects these two paths rather than serving copies.
| Document | URL |
| ---------------------------------------- | ------------------------------------------------------------------- |
| Authorization server metadata (RFC 8414) | `https://app.indexzero.site/.well-known/oauth-authorization-server` |
| Protected resource metadata (RFC 9728) | `https://app.indexzero.site/.well-known/oauth-protected-resource` |
## This manual [#this-manual]
| Document | URL | What it is |
| -------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Page index | `https://docs.indexzero.site/llms.txt` | Every page of this manual with its description. |
| Whole manual | `https://docs.indexzero.site/llms-full.txt` | Every page, concatenated. |
| Any page as markdown | append `.md` | `https://docs.indexzero.site/credits.md` is the markdown for `/credits`. `/` is `/index.md`. |
| Content negotiation | `Accept: text/markdown` | The same URL without `.md` answers with markdown when asked for it, with `Vary: Accept`. |
| Sitemap | `https://docs.indexzero.site/sitemap.xml` | Every page here. |
Every marketing page on indexzero.site also answers to `Accept: text/markdown`.
## Fetching the tool catalog [#fetching-the-tool-catalog]
```bash
curl https://app.indexzero.site/api/mcp/catalog
```
The response is a JSON array. Each entry has `name`, `description`, and `parameters` (a JSON Schema object for the tool's arguments). Descriptions state whether the tool costs credits, and the catalog also flags `costsCredits` and `readOnly` per tool, so a planner can budget before it connects.
## The MCP endpoint itself [#the-mcp-endpoint-itself]
```
https://app.indexzero.site/mcp
```
Streamable HTTP. `POST` sends a JSON-RPC 2.0 message; `GET` opens the server-to-client stream for an existing session (`Mcp-Session-Id` required); `DELETE` ends a session. Authenticate with an OAuth bearer token or an `iz_` API key. See [Any other client](/agents/other-clients) for a request that works from curl.
---
# AI visibility
> Whether assistants mention and cite a brand, share of voice against competitors, and what the same prompt gets from four models.
Source: https://docs.indexzero.site/tools/ai-visibility
Both tools **cost credits** and require a plan with AI visibility (Starter and above). Calling them on the Free plan fails with a plan-upgrade error.
## `get_brand_visibility` [#get_brand_visibility]
How often a brand is mentioned by ChatGPT and in Google's AI Overviews, its share of voice against named competitors, and which sources those answers cite.
| Argument | Type | Required | Default | Meaning |
| -------------- | ------------------------ | -------- | -------------- | -------------------------------------------------------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `query` | string, 2 to 200 chars | yes | | Brand, product, or domain to measure. Usually the project's own. |
| `competitors` | string\[], up to 9 items | no | | Competitors to compare against. Supplying these is what turns raw mention counts into a share-of-voice number. |
| `locationCode` | integer | no | project market | |
| `languageCode` | string | no | project market | |
**Returns:** total mentions, a share-of-voice table (brand, whether it is the target, mentions, share), and the 25 most-cited sources. Without `competitors` the share-of-voice table is omitted and the result says so.
ChatGPT visibility data is available for the United States and English only.
## `run_ai_prompt` [#run_ai_prompt]
Ask the same prompt of ChatGPT, Claude, Gemini, and Perplexity at once, and get each answer with its citations. Shows what an assistant actually tells someone asking about your category. Costs credits per model selected.
| Argument | Type | Required | Default | Meaning |
| ---------------------- | ------------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `prompt` | string, 3 to 4,000 chars | yes | | The prompt, exactly as a user would type it. |
| `models` | array of `chat_gpt`, `claude`, `gemini`, `perplexity`; 1 to 4 items | yes | | Which assistants to ask. Running several side by side is the point: they disagree, and the disagreement is the finding. |
| `highlightBrand` | string, up to 200 chars | no | | Brand to flag wherever it appears in the answers. |
| `webSearch` | boolean | no | `true` | Let the models search the web before answering, which is closer to how real users hit them. |
| `webSearchCountryCode` | two-letter country code | no | | Localise the models' web search. |
**Returns:** one section per model with its answer, its citations (marked when they mention the highlighted brand), and its own status. A model that fails is reported as failed without hiding the others. Responses are cached for a week, so re-running the same prompt within that window is free.
---
# Domains and backlinks
> Size up any domain, yours or a competitor's. Traffic, ranked keywords, top pages, SERP competitors, and the link profile.
Source: https://docs.indexzero.site/tools/domains-and-backlinks
All six tools **cost credits**. All take an optional `domain`; when omitted they analyse the project's own domain, so the same call compares a competitor by adding one argument. Backlink tools are market-independent and take no location or language.
## `get_domain_overview` [#get_domain_overview]
A domain's organic footprint: estimated traffic, ranking keyword count, traffic value, and how its rankings spread across position buckets. The fastest way to size up a competitor.
| Argument | Type | Required | Default |
| -------------- | ------- | -------- | -------------- |
| `projectId` | string | yes | |
| `domain` | string | no | project domain |
| `locationCode` | integer | no | project market |
| `languageCode` | string | no | project market |
**Returns:** organic keyword count, estimated monthly organic traffic, estimated traffic value in USD per month, and a distribution of rankings by position bucket.
## `get_ranked_keywords` [#get_ranked_keywords]
Every keyword a domain already ranks for, with its position, search volume, and the ranking URL. Use it to find a competitor's winners or your own near-misses.
| Argument | Type | Required | Default |
| -------------- | ------------------ | -------- | -------------- |
| `projectId` | string | yes | |
| `domain` | string | no | project domain |
| `locationCode` | integer | no | project market |
| `languageCode` | string | no | project market |
| `limit` | integer, 1 to 1000 | no | 100 |
## `get_domain_top_pages` [#get_domain_top_pages]
The pages driving a domain's organic traffic, ranked by estimated traffic. Shows what content actually works for a competitor.
Arguments are the same as `get_ranked_keywords`.
## `find_serp_competitors` [#find_serp_competitors]
Domains that rank alongside you across a set of keywords, with how many keywords they intersect on and their average position. Identifies real search competitors rather than assumed business ones. The comparison anchor is the project, so there is no `domain` argument.
| Argument | Type | Required | Default | Meaning |
| -------------- | ------------------------- | -------- | -------------- | ------------------------------------------------------------------------------ |
| `projectId` | string | yes | | |
| `keywords` | string\[], 1 to 200 items | yes | | Keywords to find shared competitors for. More keywords give a sharper picture. |
| `locationCode` | integer | no | project market | |
| `languageCode` | string | no | project market | |
| `limit` | integer, 1 to 200 | no | 50 | |
**Returns:** domain, intersections, average position, keyword count, and estimated traffic per competitor.
## `get_backlinks_overview` [#get_backlinks_overview]
Summary of a domain's backlink profile: total backlinks, referring domains, rank, broken links, and recent new and lost movement. Includes an Ahrefs domain rating when available, at no extra cost.
| Argument | Type | Required | Default |
| ----------- | ------ | -------- | -------------- |
| `projectId` | string | yes | |
| `domain` | string | no | project domain |
## `get_backlinks_profile` [#get_backlinks_profile]
The rows behind the summary: individual links, referring domains, or the most-linked pages. Use `get_backlinks_overview` first for totals, then this to inspect.
| Argument | Type | Required | Default | Meaning |
| ----------- | ------------------------------------------------ | -------- | -------------- | ---------------------------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `domain` | string | no | project domain | |
| `view` | `backlinks`, `referring_domains`, or `top_pages` | no | `backlinks` | Which slice to return: individual links, linking sites, or your most-linked pages. |
| `limit` | integer, 1 to 1000 | no | 100 | |
Columns depend on the view; each row of the backlinks view carries a spam score.
---
# Tool reference
> Every MCP tool, grouped the way an agent works through them, with what each costs.
Source: https://docs.indexzero.site/tools
The MCP server exposes tools. They are the same operations the dashboard runs, so anything you can do by clicking, an agent can do by calling. This section documents each tool's arguments, defaults, cost, and behaviour.
## Conventions shared by every tool [#conventions-shared-by-every-tool]
**`projectId`.** Every tool except `whoami`, `list_projects`, and `create_project` takes a `projectId`. Get it from `list_projects`. It scopes the call, supplies the default domain and market, and attributes credit spend. A project id that does not belong to the workspace fails with `Project not found in this workspace.` whether it exists or not.
**Market defaults.** Tools that take `locationCode` (a location code; 2840 is the United States) and `languageCode` (an ISO code such as `en`) fall back to the project's market when you omit them. Pass them to research a different country without changing the project.
**Domain defaults.** Domain and backlink tools take an optional `domain`. When omitted they analyse the project's own domain; pass a competitor's to compare. Domains are bare (`example.com`), with no protocol or path.
**Limits.** Tools that return rows take an optional `limit` with a stated maximum and default. Rows beyond the limit are not returned, and the tool says how many it dropped.
**Cost.** Each tool's description says whether it costs credits. Free tools are marked read-only in their annotations. See [How credits work](/credits).
**Results.** Every result carries the full rows as a text table and as structured content, plus `_meta` with the organization and project ids, a dashboard deep link where relevant, and for paid calls the credits charged and remaining. Missing values render as `—`, booleans as `yes`/`no`, decimals to two places.
**Errors.** Failures come back as tool results marked as errors with a code, a message, and a hint. See [Errors](/reference/errors).
## Free versus paid [#free-versus-paid]
| Free (no provider spend) | Costs credits |
| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `whoami`, `list_projects`, `create_project` | `research_keywords`, `get_keyword_metrics` |
| `save_keywords`, `list_saved_keywords` | `get_domain_overview`, `get_ranked_keywords`, `get_domain_top_pages`, `find_serp_competitors` |
| `get_rank_tracker`, `estimate_rank_tracker_cost`, `add_rank_tracking_keywords`, `remove_rank_tracking_keywords` | `get_backlinks_overview`, `get_backlinks_profile` |
| `get_audit_status`, `get_audit_issues`, `get_audit_pages` | `search_local_businesses`, `get_local_serp_results`, `get_google_business_questions` |
| All Search Console, Google Analytics, and PostHog tools | `run_rank_tracker`, `run_site_audit` |
| | `get_brand_visibility`, `run_ai_prompt` |
## Asynchronous tools [#asynchronous-tools]
Two tools start work and return before it finishes:
* `run_site_audit` returns an `auditId`. Poll `get_audit_status` until it reports `completed`, then read `get_audit_issues` and `get_audit_pages`.
* `run_rank_tracker` returns a `runId`. Poll `get_rank_tracker` for positions.
Both run the same background workflows the dashboard uses, so a run started by an agent shows up in the app with live progress.
---
# Keyword research
> Turn seeds into scored keywords, look up metrics for a list you already have, and save the ones worth keeping.
Source: https://docs.indexzero.site/tools/keyword-research
Volume is monthly searches. KD is keyword difficulty from 0 to 100. CPC is in USD. Competition is paid competition from 0 to 1. A `—` in any cell means the provider had no value.
## `research_keywords` [#research_keywords]
Expand seed keywords into related keywords with search volume, difficulty, CPC, competition, and intent. **Costs credits**, roughly 30 to 100 per seed depending on the data source.
| Argument | Type | Required | Default | Meaning |
| ------------------------ | --------------------------------------------- | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `projectId` | string | yes | | |
| `seeds` | string\[], 1 to 5 items, each up to 200 chars | yes | | Seed keywords, researched together in one call. Prefer one call with several seeds over several single-seed calls. |
| `locationCode` | integer | no | project market | |
| `languageCode` | string | no | project market | |
| `limit` | integer, 1 to 500 | no | 100 | Maximum rows to return. |
| `includeClickstreamData` | boolean | no | `false` | Refine volumes with clickstream data, which splits Google Ads' grouped close variants (plurals, misspellings). **Doubles the credit cost.** |
**Returns:** the keyword rows, the provider and sources used, and the cost in USD. Results are also written to the project's metrics cache, so saving them later and viewing them in the dashboard does not buy the data twice.
In countries whose data comes from Google Ads rather than a clickstream source, difficulty and intent are unavailable and `includeClickstreamData` has no effect.
## `get_keyword_metrics` [#get_keyword_metrics]
Look up volume, difficulty, CPC, and competition for an exact list of keywords, with no expansion. Use it to score a list you already have; use `research_keywords` to discover new ones. **Costs credits** per batch.
| Argument | Type | Required | Default | Meaning |
| -------------- | ----------------------------------------------- | -------- | -------------- | ---------------------------------------------------- |
| `projectId` | string | yes | | |
| `keywords` | string\[], 1 to 700 items, each up to 200 chars | yes | | Exact terms to look up. Batch up to 700 in one call. |
| `locationCode` | integer | no | project market | |
| `languageCode` | string | no | project market | |
**Returns:** one row per keyword that had data, and an explicit list of the keywords the provider returned nothing for, so a missing row is never mistaken for a zero.
## `save_keywords` [#save_keywords]
Add keywords to the project's saved list, where you can tag and track them in the dashboard. **Free**; no provider call. Duplicates in the same market are ignored, so the call is safe to repeat.
| Argument | Type | Required | Default | Meaning |
| -------------- | ------------------------- | -------- | -------------- | ----------------- |
| `projectId` | string | yes | | |
| `keywords` | string\[], 1 to 500 items | yes | | Keywords to save. |
| `locationCode` | integer | no | project market | |
| `languageCode` | string | no | project market | |
## `list_saved_keywords` [#list_saved_keywords]
The project's saved keywords with their cached metrics. **Free**; reads stored data, so metrics are as fresh as the last research or refresh that touched them.
| Argument | Type | Required | Default | Meaning |
| ----------- | ----------------------- | -------- | ------- | ------------------------------------- |
| `projectId` | string | yes | | |
| `search` | string, up to 200 chars | no | | Substring filter on the keyword text. |
---
# Local search
> Google Business listings, Maps and Local Finder rankings, and the questions people ask on a listing, at a coordinate.
Source: https://docs.indexzero.site/tools/local-search
Local results depend on where the searcher stands, so all three tools require a `locationCoordinate`: a string of `latitude,longitude,zoom`, for example `30.2672,-97.7431,13`. There is no sensible default. All three **cost credits**.
## `search_local_businesses` [#search_local_businesses]
Google Business listings near a coordinate, by name or category: title, address, rating, and review count. Use it for local competitor research and citation checks.
| Argument | Type | Required | Default | Meaning |
| -------------------- | ------------------------- | ------------------------------ | ------- | ------------------------------------------------------ |
| `projectId` | string | yes | | |
| `locationCoordinate` | string | yes | | `lat,lng,zoom`. |
| `title` | string, up to 200 chars | one of `title` or `categories` | | Business name to search for. |
| `categories` | string\[], up to 10 items | one of `title` or `categories` | | Google Business categories, for example `["plumber"]`. |
| `limit` | integer, 1 to 100 | no | 20 | |
## `get_local_serp_results` [#get_local_serp_results]
Google Maps or Local Finder rankings for a keyword at a coordinate: who occupies the local pack there.
| Argument | Type | Required | Default | Meaning |
| -------------------- | ------------------------ | -------- | ---------------- | ------------------------------------------------------- |
| `projectId` | string | yes | | |
| `keyword` | string, 1 to 200 chars | yes | | Search term. |
| `locationCoordinate` | string | yes | | `lat,lng,zoom`. |
| `languageCode` | string | no | project language | |
| `searchType` | `maps` or `local_finder` | no | `maps` | The Google Maps result set, or the expanded local pack. |
| `depth` | integer, 1 to 100 | no | 20 | How many results to read. |
## `get_google_business_questions` [#get_google_business_questions]
Questions and answers posted on a Google Business Profile. A direct read on what customers actually ask; good raw material for FAQ and content work.
| Argument | Type | Required | Default | Meaning |
| -------------------- | ---------------------- | -------- | ---------------- | --------------------------------------------------- |
| `projectId` | string | yes | | |
| `keyword` | string, 1 to 200 chars | yes | | Business name or search term to pull questions for. |
| `locationCoordinate` | string | yes | | `lat,lng,zoom`. |
| `languageCode` | string | no | project language | |
| `depth` | integer, 1 to 100 | no | 20 | |
---
# PostHog
> Sessions, pages, acquisition, events, and conversions from a connected PostHog project. Free.
Source: https://docs.indexzero.site/tools/posthog
These three tools read a PostHog project connected to the IndexZero project, by running HogQL over its sessions and events tables. They are **free** and read-only. They are the PostHog counterparts of the Google Analytics tools; use whichever the project has connected. Connecting is done in the dashboard with a personal API key; see [Integrations](/guides/integrations).
## Shared arguments [#shared-arguments]
| Argument | Type | Required | Default | Meaning |
| ---------------------- | ------------------------- | -------- | ---------------- | ---------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `channel` | `organic_search` or `all` | no | `organic_search` | Sessions PostHog attributes to organic search, or every channel. |
| `days` | integer, 1 to 365 | no | 28 | Trailing window. |
| `startDate`, `endDate` | `YYYY-MM-DD` | no | | Explicit window. |
## `get_posthog_overview` [#get_posthog_overview]
Sessions, visitors, pageviews, bounce rate, and average session duration for the window, with the previous equal-length window for comparison and a daily trend.
Arguments: the shared set.
## `get_posthog_report` [#get_posthog_report]
Run one report.
| Argument | Type | Required | Default | Meaning |
| ---------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `kind` | `entry_pages`, `top_pages`, `exit_pages`, `traffic_acquisition`, `events`, `conversions`, or `audience` | yes | | `entry_pages` is landing pages by session, `top_pages` by pageview. |
| `acquisitionBreakdown` | `channel`, `referring_domain`, `utm_source`, `utm_medium`, or `utm_campaign` | no | `channel` | For `traffic_acquisition`. |
| `audienceBreakdown` | `device_type`, `country`, `browser`, or `os` | no | `device_type` | For `audience`. |
| `eventName` | string, 1 to 200 chars | required by `conversions` | | The PostHog event that counts as a conversion, for example `signup_completed`. Run the `events` report first to find real names. |
| `pathPrefix` | string, up to 200 chars | no | | Restrict to paths starting with this, for example `/blog`. |
| `host` | string, up to 253 chars | no | every host | Restrict to one hostname. Useful when the PostHog project has also seen localhost or staging traffic. |
| `limit` | integer, 1 to 500 | no | 100 | |
Plus the shared arguments.
## `get_posthog_search_opportunities` [#get_posthog_search_opportunities]
Joins Search Console against PostHog to find pages that attract search traffic but under-convert, and queries with impressions that are not landing clicks. Requires Search Console and PostHog both connected.
| Argument | Type | Required | Default | Meaning |
| ----------- | ----------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `projectId` | string | yes | | |
| `eventName` | string | no | | The conversion event. With it, under-performance means "ranks but never converts"; without it, the report falls back to bounce rate. |
| `days` | integer, 1 to 365 | no | 28 | |
---
# Rank tracking
> Create trackers, estimate a check before running it, add and remove keywords, and read positions over time.
Source: https://docs.indexzero.site/tools/rank-tracking
A tracker pins a domain, a set of keywords, a market, a device choice, and a SERP depth, and records positions each time it is checked. Creating, editing, and reading trackers is free. **Running a check costs credits**, and the cost is known exactly before you commit: see [the cost model](#the-cost-model).
## `create_rank_tracker` [#create_rank_tracker]
Create a tracker. Free to create; each check costs credits. Call `estimate_rank_tracker_cost` afterwards and report the number before committing anyone to a schedule.
| Argument | Type | Required | Default | Meaning |
| ------------------ | ------------------------------------------------- | -------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `keywords` | string\[], 1 to 1,000 items, each up to 200 chars | yes | | Keywords to track. |
| `domain` | string | no | project domain | Domain to track. |
| `locationCode` | integer | no | project market | |
| `languageCode` | string | no | project market | |
| `devices` | `both`, `desktop`, or `mobile` | no | `both` | `both` doubles the cost of every check. |
| `serpDepth` | `10`, `20`, `30`, `50`, or `100` | no | `100` | How deep to read the SERP. Deeper costs more. The dashboard's default is 20. |
| `scheduleInterval` | `daily`, `weekly`, `monthly`, or `manual` | no | `manual` | `manual` never runs by itself and is the only option on the Free plan; the others require a paid plan. |
| `locationName` | string, up to 255 chars | no | national | A canonical location name such as `Austin,Texas,United States` switches the tracker to city-level results. Omit for national tracking. |
Subject to the plan's tracker limit (workspace-wide) and, for a schedule other than `manual`, to the scheduled-tracking plan feature. The project must have a domain or the call must pass one.
## `get_rank_tracker` [#get_rank_tracker]
List the project's trackers, or inspect one. **Free**; reads stored snapshots. This is also the tool to poll after `run_rank_tracker`.
| Argument | Type | Required | Meaning |
| ----------- | ------ | -------- | -------------------------------------------------------------- |
| `projectId` | string | yes | |
| `configId` | string | no | Tracker to inspect. Omit to list every tracker on the project. |
With a `configId`, returns the settings, the tracked keywords with ids, the latest position per keyword and device, and the last 10 runs. A blank position means the domain was not found within the tracker's depth.
## `estimate_rank_tracker_cost` [#estimate_rank_tracker_cost]
What one check of a tracker will cost in credits, and roughly how long it will take. **Free.** Call it before `run_rank_tracker`: a large tracker can cost thousands of credits per run.
| Argument | Type | Required | Default | Meaning |
| ----------- | ----------------------- | -------- | -------- | --------------------------------------------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `configId` | string | yes | | |
| `trigger` | `manual` or `scheduled` | no | `manual` | `manual` prices a live check (faster, about three times the cost); `scheduled` prices a queued one. |
**Returns:** `keywordCount`, `checks` (keywords times devices), `costCredits`, `costUsd`, `estimatedSeconds`, and the `method` priced.
## `add_rank_tracking_keywords` [#add_rank_tracking_keywords]
Add keywords to an existing tracker. Free to add, but every future check costs more; re-run the estimate afterwards.
| Argument | Type | Required |
| ----------- | --------------------------- | -------- |
| `projectId` | string | yes |
| `configId` | string | yes |
| `keywords` | string\[], 1 to 1,000 items | yes |
## `remove_rank_tracking_keywords` [#remove_rank_tracking_keywords]
Stop tracking keywords. Position history is kept, so removing and re-adding a keyword loses nothing.
| Argument | Type | Required | Meaning |
| ------------ | --------------------------- | -------- | ------------------------------------------------------------------ |
| `projectId` | string | yes | |
| `configId` | string | yes | |
| `keywordIds` | string\[], 1 to 1,000 items | yes | Tracked-keyword ids from `get_rank_tracker`, not the keyword text. |
## `run_rank_tracker` [#run_rank_tracker]
Trigger an immediate live position check. **Spends credits, often a lot.** Returns at once with a `runId`; poll `get_rank_tracker` for results.
| Argument | Type | Required | Meaning |
| ---------------- | ------- | -------- | --------------------------------------------------------------------- |
| `projectId` | string | yes | |
| `configId` | string | yes | |
| `maxCostCredits` | integer | no | Refuse to run, at no cost, if the estimate exceeds this many credits. |
The budget is checked before anything is launched, and again inside the run against a fresh estimate, so a stale estimate cannot overspend. Only one check per tracker can be in flight; a second trigger while one is running fails rather than double-spending.
## The cost model [#the-cost-model]
Rank checks are the one operation with a published, pre-computed price. One **check** is one keyword on one device at the tracker's depth. Depth 10 is one page of results, depth 20 two pages, and so on.
| | First page | Each additional page |
| ------------------------- | ---------- | -------------------- |
| Live (manual checks) | $0.002 | $0.0015 |
| Queued (scheduled checks) | $0.0006 | $0.00045 |
Each metered call is marked up by 1.28, rounded to five decimals, and converted at 1,000 credits per dollar, rounding up per call. Live checks are one call per check; queued checks are batched up to 100 per call, which is why scheduled tracking is about 30% of the price of a manual run.
Worked example, 100 keywords, both devices (200 checks), depth 20:
* **Manual run:** each check costs ($0.002 + $0.0015) × 1.28 = $0.00448, which rounds up to 5 credits. 200 checks cost **1,000 credits**.
* **Scheduled run:** each check costs ($0.0006 + $0.00045) × 1.28 = $0.001344. A batch of 100 checks is $0.1344, or 135 credits. 200 checks cost **270 credits**; daily, that is about 8,100 credits a month; weekly, about 1,080.
The estimate tool does this arithmetic for you and is exact, with one caveat: a queued task that is rejected or times out can incur a live fallback, so a scheduled month can cost slightly more than the nominal figure.
---
# Search Console and Google Analytics
> Your own measured search performance and what visitors did next. Free, because it is your data.
Source: https://docs.indexzero.site/tools/search-console-and-analytics
These five tools read data from a Google Search Console property or a Google Analytics 4 property connected to the project. They are **free** and marked read-only. They fail with a `NOT_CONNECTED` or `RECONNECT_REQUIRED` error, with a hint, when the integration is missing or its access has lapsed. Connecting is done in the dashboard; see [Integrations](/guides/integrations).
When the question is about the user's own site, prefer these over paid estimates: they are measured, not modelled.
## Shared date arguments [#shared-date-arguments]
`get_search_console_performance` and `get_striking_distance` take the same window:
| Argument | Type | Required | Default | Meaning |
| ----------- | ------------------------------------------------------------------------------------------------------ | -------- | -------------- | -------------------------------------- |
| `projectId` | string | yes | | |
| `dateRange` | `last_7_days`, `last_28_days`, `last_3_months`, `last_6_months`, `last_12_months`, or `last_16_months` | no | `last_28_days` | Preset window. |
| `startDate` | `YYYY-MM-DD` | no | | Explicit start; overrides `dateRange`. |
| `endDate` | `YYYY-MM-DD` | no | | Explicit end. |
| `type` | `web`, `image`, `video`, `news`, `googleNews`, or `discover` | no | `web` | Search surface. |
Search Console data lags by about three days; preset ranges end before the lag.
## `get_search_console_performance` [#get_search_console_performance]
Clicks, impressions, CTR, and average position, broken down by query, page, and date, with the previous period for comparison. The text lane shows totals and the top 50 queries; the structured result carries every row by query, page, and date.
## `get_striking_distance` [#get_striking_distance]
Queries where the site already ranks in positions 5 to 20, the ones closest to page-one traffic, with the page currently ranking for each. These need a nudge, not new content.
## `inspect_urls` [#inspect_urls]
Google's own index status for specific URLs: whether they are indexed, when last crawled, the canonical Google chose, mobile usability, and rich-result status.
| Argument | Type | Required | Meaning |
| ----------- | ------------------------ | -------- | ---------------------------------- |
| `projectId` | string | yes | |
| `urls` | string\[], 1 to 10 items | yes | Must be on the connected property. |
## `get_analytics_report` [#get_analytics_report]
Run one of the project's GA4 reports. Pairs with Search Console: Search Console says how people arrived, GA4 says what they did next.
| Argument | Type | Required | Default | Meaning |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `projectId` | string | yes | | |
| `kind` | `landing_pages`, `page_performance`, `key_events`, `traffic_acquisition`, `ecommerce_performance`, `site_search`, or `audience_breakdown` | yes | | `landing_pages` and `page_performance` answer "which pages work"; `traffic_acquisition` compares channels; `key_events` and `ecommerce_performance` cover conversions. |
| `channel` | `organic_search` or `all` | no | `organic_search` | |
| `days` | integer, 1 to 365 | no | 28 | Trailing window. |
| `startDate`, `endDate` | `YYYY-MM-DD` | no | | Explicit window. |
| `limit` | integer, 1 to 500 | no | 100 | |
## `get_search_opportunities` [#get_search_opportunities]
Joins Search Console against Analytics to find pages that attract search traffic but under-convert, and queries with impressions that are not landing clicks. Requires both integrations.
| Argument | Type | Required | Default |
| ----------- | ----------------- | -------- | ------- |
| `projectId` | string | yes | |
| `days` | integer, 1 to 365 | no | 28 |
---
# Site audits
> Crawl your own site for technical and on-page issues, with Lighthouse on a sample of pages.
Source: https://docs.indexzero.site/tools/site-audits
An audit crawls the project's site, checks every page against issue types, and runs Lighthouse on a sample. Starting a crawl **costs credits** scaling with page count; reading the results is free. The crawl is asynchronous and takes minutes.
## `run_site_audit` [#run_site_audit]
Start a crawl. Returns an `auditId` immediately. Poll `get_audit_status` until it reports `completed`, then read `get_audit_issues`.
| Argument | Type | Required | Default | Meaning |
| ------------ | ------------------------- | -------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `projectId` | string | yes | | The project must have a domain, or `startUrl` must be given. |
| `startUrl` | string, up to 2,048 chars | no | `https://` plus the project domain | Where to start. The crawl stays on the same origin; redirects from the start URL are followed first, so an apex that redirects to `www` anchors on the real origin. |
| `maxPages` | integer, 10 to 10,000 | no | 50 | Page budget. A value above the plan's cap is trimmed to the cap rather than rejected, and the response says so. |
| `lighthouse` | boolean | no | `true` | Also run Lighthouse on a sample of pages, mobile and desktop. Adds cost and time. |
**Returns:** `auditId`, the effective `maxPages`, and `clampedByPlan` when the budget was trimmed.
### How the crawler behaves [#how-the-crawler-behaves]
* Identifies as `IndexZero-Audit/1.0`. If your bot protection challenges it, pages are reported as **blocked** rather than broken, and the fix is to allowlist that user agent.
* Reads and obeys `robots.txt` for that user agent, and seeds the crawl from the sitemaps it lists.
* Stays on the start URL's origin. Private, loopback, and cloud-metadata addresses are refused, and hostnames are resolved to check they do not point at one.
* Adapts its concurrency to the site: starts at 10 parallel fetches, backs off when pages are slow, blocked, or erroring, and grows when the site is responding well.
* Reads at most 1 MiB of HTML per page with a 15-second timeout.
### Lighthouse sampling [#lighthouse-sampling]
The sample is the homepage plus one representative page per URL template, up to 10 URLs, chosen from pages that returned 2xx. Each is run twice, mobile and desktop, so Lighthouse adds up to 20 paid calls. Captured per run: performance, accessibility, best-practices, and SEO scores, plus LCP, CLS, INP, and TTFB.
### Phases [#phases]
`discovery`, then `crawling`, then `lighthouse`, then `finalizing`, ending in `completed` or `failed`. `get_audit_status` reports the current phase and pages crawled so far. An audit that fails reports why; the most common cause is a site that takes too long to respond, and the suggested fix is a smaller page limit.
## `get_audit_status` [#get_audit_status]
Progress of a crawl: phase, pages crawled, issue count, and whether it finished. **Free.** Do not report on issues until status is `completed`; before that the numbers are partial.
| Argument | Type | Required | Meaning |
| ----------- | ------ | -------- | ----------------------------------------- |
| `projectId` | string | yes | |
| `auditId` | string | no | Omit for the project's most recent audit. |
## `get_audit_issues` [#get_audit_issues]
The issues a completed audit found, with the URL and detail for each. **Free.** Filter by severity to triage, critical first. Each issue type has a known fix; an agent should ask before changing a site.
| Argument | Type | Required | Default | Meaning |
| ----------- | -------------------------------- | -------- | ------------ | ------------------------------------------------------ |
| `projectId` | string | yes | | |
| `auditId` | string | no | latest audit | |
| `severity` | `critical`, `warning`, or `info` | no | all | |
| `issueType` | one of the issue type ids | no | all | See [Audit issue types](/reference/audit-issue-types). |
| `limit` | integer, 1 to 1,000 | no | 200 | |
**Returns:** the issue rows and counts by severity.
## `get_audit_pages` [#get_audit_pages]
The pages a crawl visited, with status code, title, word count, and H1s. **Free.** Use it to see what was actually reachable and to spot thin or untitled pages.
| Argument | Type | Required | Default |
| ----------- | ------------------- | -------- | ------------ |
| `projectId` | string | yes | |
| `auditId` | string | no | latest audit |
| `limit` | integer, 1 to 1,000 | no | 100 |
---
# Workspace
> Orient a session. Who am I, which projects exist, create one.
Source: https://docs.indexzero.site/tools/workspace
These three tools are free and are where every session starts.
## `whoami` [#whoami]
Identity and credit balance for the connected workspace. Call it first when you need to know whether the workspace can afford a batch of research, or which plan's limits apply.
**Arguments:** none.
**Returns:** the workspace, the plan, `creditsRemaining`, `monthlyCredits`, `topupCredits`, and `periodEnd` (when the monthly allowance resets). The text lane reminds the agent that 1,000 credits is roughly $1 of provider spend. `_meta` deep-links to the billing page.
## `list_projects` [#list_projects]
The workspace's active projects. Every other tool needs a `projectId` from here.
**Arguments:** none.
**Returns:** one row per project with `projectId`, name, domain, location, language, and whether it is the default. Archived projects are not listed. Ordered newest first.
## `create_project` [#create_project]
Create a project. Free, but subject to the plan's limit on active projects; the call fails with an upgrade message if that limit is reached.
| Argument | Type | Required | Default | Meaning |
| -------------- | ----------------------- | -------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string, 1 to 80 chars | yes | | Display name. |
| `domain` | string, up to 255 chars | no | none | Bare domain the project tracks. Required before audits, rank tracking, or own-site backlink tools will work. A URL is accepted and reduced to its hostname. |
| `locationCode` | integer | no | `2840` (United States) | Default location for research. |
| `languageCode` | string, 2 to 10 chars | no | `"en"` | Default language for research. |
**Returns:** the new project's id and settings. An invalid domain fails with `"" is not a valid domain.`