> ## Documentation Index
> Fetch the complete documentation index at: https://docs.topify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Free automatic AI agent integration

> AI-agent-friendly API — automatically create a free Topify account, get an API key, and start brand tracking in 3 calls. No human involvement needed. No browser, no verification, no payment. Built for Claude Code, Cursor, and any tool-use LLM agent.

<Columns cols={3}>
  <Card title="Claude Code" icon="terminal">
    Use tool\_use to call the endpoints below directly from Claude Code.
  </Card>

  <Card title="Cursor" icon="code">
    Cursor's agent mode can call these endpoints via HTTP requests.
  </Card>

  <Card title="OpenClaw" icon="robot">
    Any tool-use-capable LLM agent can complete this flow.
  </Card>
</Columns>

These endpoints let AI agents set up Topify.ai from scratch and generate a full GEO visibility report -- all without requiring the end user to visit a browser, verify an email, or touch the dashboard.

The full flow is three API calls:

1. **Create account** -- register with an email, receive credentials instantly
2. **Get API key** -- exchange credentials for an API key
3. **Create project** -- start brand tracking and receive results via webhook

<Tip>
  If the user provides their email, use it. If not, **you (the agent) should generate a temporary email yourself** using the format `temp-{random}@topify.ai` — just create a random hex string (e.g. `temp-a8f3b1c9@topify.ai`) and pass it to the create account endpoint. The server does not generate this for you. This creates a fully functional anonymous account with zero user input required. The user can link a real email later from the dashboard.
</Tip>

<Note>
  These endpoints are designed for **tool-use AI agents** such as Claude Code, Cursor, and OpenClaw that can make HTTP requests programmatically. If you are using a web-based AI chatbot (ChatGPT, Perplexity, etc.) that cannot call APIs directly, create your account and project manually at [app.topify.ai](https://app.topify.ai), then use the **Get API key** endpoint with your email and password to obtain a key for the read-only data endpoints.
</Note>

***

## Credits and rate limits

Every API key has a **rate limit tier** and a **monthly credit allowance**. Credits are consumed when projects fetch AI responses during bootstrap and daily refresh cycles.

### Rate limit tiers

Rate limits use a sliding 60-second window. The burst allowance lets you briefly exceed the base rate.

| Tier     | Requests / minute | Burst | Effective limit / window |
| -------- | ----------------- | ----- | ------------------------ |
| Standard | 60                | +10   | 70                       |
| Premium  | 300               | +50   | 350                      |

New API keys are created on the **standard** tier. Contact us to upgrade to premium.

When the limit is exceeded, the API returns `429 Too Many Requests` with a `Retry-After` header indicating how many seconds to wait.

### Research credits by plan

Each plan includes a monthly credit budget that determines how many AI research queries your projects can run.

| Plan                                          | Monthly credits |
| --------------------------------------------- | --------------- |
| Skip Trial (default for API-created accounts) | 10              |
| Basic                                         | 200             |
| Pro                                           | 500             |
| Enterprise                                    | 2,000           |

Accounts created via the API start on the **skip\_trial** plan with 10 credits. Each project bootstrap consumes credits proportional to the number of prompts and AI providers queried (typically 5 prompts across 3 providers = 15 credits per full bootstrap cycle). The skip\_trial plan runs a reduced bootstrap that fits within the 10-credit budget. For a full bootstrap with all prompts and providers, upgrade to the Basic plan or higher from the [dashboard](https://app.topify.ai) or contact us.

### Response headers

Every response includes rate limit and credit information:

| Header                  | Description                                     |
| ----------------------- | ----------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window  |
| `X-RateLimit-Remaining` | Requests remaining before throttling            |
| `X-Credit-Available`    | Research credits remaining on your key          |
| `Retry-After`           | Seconds until the window resets (only on `429`) |

***

## Create account

```
POST /account/create
```

Creates a new Topify.ai account with a team on the `skip_trial` plan. The account is created without email verification, so it can be used immediately.

<Note>
  No authentication is required for this endpoint. The generated password is returned once and cannot be retrieved later -- store it securely.
</Note>

### Request body

| Field      | Type   | Required | Description                                                                                                                                                          |
| ---------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`    | string | Yes      | Email address for the new account. Use the user's email if available, or the agent should generate `temp-{random}@topify.ai` (random hex string) for anonymous setup |
| `password` | string | No       | Ignored. A secure password is generated server-side                                                                                                                  |

```json theme={null}
{
  "email": "user@example.com"
}
```

### Response

```json theme={null}
{
  "code": 200,
  "message": "200 OK",
  "data": {
    "user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "email": "user@example.com",
    "password": "Tp8f2a1b3c4d5e6f7a8b9c0d1e2f3a4b516384!"
  }
}
```

### Response fields

| Field      | Type          | Description                                  |
| ---------- | ------------- | -------------------------------------------- |
| `user_id`  | string (UUID) | The new user's ID                            |
| `team_id`  | string (UUID) | The team created for this account            |
| `email`    | string        | The normalized email address                 |
| `password` | string        | Auto-generated password. Store this securely |

### Errors

| Status | Detail                                                           | Cause                                 |
| ------ | ---------------------------------------------------------------- | ------------------------------------- |
| `400`  | `A valid email is required`                                      | Email is empty or missing `@`         |
| `409`  | `Failed to create account. The email may already be registered.` | A user with this email already exists |

<Tip>
  **If you receive a 409:** The account already exists. Skip to the **Get API key** step using the same email and the user's existing password. If the password is unknown, ask the user or direct them to reset it at [app.topify.ai](https://app.topify.ai).
</Tip>

***

## Get API key

```
POST /account/api-key
```

Authenticates with email and password, then returns an API key for the account. If no key exists, one is created automatically on the **standard** rate limit tier.

### Request body

| Field      | Type   | Required | Description                                 |
| ---------- | ------ | -------- | ------------------------------------------- |
| `email`    | string | Yes      | Account email address                       |
| `password` | string | Yes      | Account password (from the create endpoint) |

```json theme={null}
{
  "email": "user@example.com",
  "password": "Tp8f2a1b3c4d5e6f7a8b9c0d1e2f3a4b516384!"
}
```

### Response

```json theme={null}
{
  "code": 200,
  "message": "200 OK",
  "data": {
    "api_key": "tk_live_214d48ed311144ee850192347ff32a2c",
    "team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "key_prefix": "tk_live_214d",
    "rate_limit_tier": "standard"
  }
}
```

### Response fields

| Field             | Type          | Description                                                       |
| ----------------- | ------------- | ----------------------------------------------------------------- |
| `api_key`         | string        | Full API key. Store this securely -- it cannot be retrieved again |
| `team_id`         | string (UUID) | The team this key belongs to                                      |
| `key_prefix`      | string        | First 12 characters of the key, for identification                |
| `rate_limit_tier` | string        | Rate limit tier assigned to this key (`standard` or `premium`)    |

### Errors

| Status | Detail                               | Cause                         |
| ------ | ------------------------------------ | ----------------------------- |
| `401`  | `Invalid email or password`          | Authentication failed         |
| `500`  | `Authentication service unavailable` | Supabase auth service is down |

***

## Create project

```
POST /account/projects
```

Creates a new brand tracking project and starts the bootstrap pipeline in the background. The endpoint returns immediately with a `202 Accepted` status while the pipeline runs asynchronously.

The bootstrap pipeline generates tracking prompts, fetches initial AI responses from all providers, calculates brand metrics, and detects competitors. When complete, a webhook callback is sent to the URL you provide.

<Note>
  Requires API key authentication via the `X-API-Key` header.
</Note>

### Request body

| Field         | Type   | Required | Description                                      |
| ------------- | ------ | -------- | ------------------------------------------------ |
| `brand_name`  | string | Yes      | The brand name to track                          |
| `brand_url`   | string | Yes      | The brand's website URL                          |
| `webhook_url` | string | Yes      | URL to receive the completion callback           |
| `language`    | string | No       | Language for generated prompts (e.g. `en`, `ja`) |
| `location`    | string | No       | Target market country code (e.g. `US`, `JP`)     |

```json theme={null}
{
  "brand_name": "Acme Corp",
  "brand_url": "https://acme.com",
  "webhook_url": "https://your-service.com/webhooks/topify",
  "language": "en",
  "location": "US"
}
```

### Response

```json theme={null}
{
  "code": 202,
  "message": "202 Accepted",
  "data": {
    "project_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "status": "initializing"
  }
}
```

### Response fields

| Field        | Type          | Description                           |
| ------------ | ------------- | ------------------------------------- |
| `project_id` | string (UUID) | The new project's ID                  |
| `status`     | string        | Initial status. Always `initializing` |

### Webhook callback

When the bootstrap pipeline completes (or fails), a POST request is sent to your `webhook_url` with the following payload:

**Success:**

```json theme={null}
{
  "event": "project.bootstrap",
  "project_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "status": "completed",
  "error": null,
  "timestamp": "2026-03-10T03:27:35Z"
}
```

**Failure:**

```json theme={null}
{
  "event": "project.bootstrap",
  "project_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "status": "error",
  "error": "Failed to generate prompts",
  "timestamp": "2026-03-10T03:27:35Z"
}
```

#### Webhook headers

Callbacks are signed using the [Standard Webhooks](https://www.standardwebhooks.com/) specification. Verify the signature to ensure the callback is authentic.

| Header              | Description                                                 |
| ------------------- | ----------------------------------------------------------- |
| `webhook-id`        | Unique message ID (`msg_<uuid>`)                            |
| `webhook-timestamp` | Unix timestamp when the message was sent                    |
| `webhook-signature` | HMAC-SHA256 signature in the format `v1,<base64-signature>` |

The signature is computed over the string `{webhook-id}.{webhook-timestamp}.{body}` using your shared webhook signing secret.

### Errors

| Status | Detail                                             | Cause                         |
| ------ | -------------------------------------------------- | ----------------------------- |
| `400`  | `brand_name is required`                           | Missing or empty brand name   |
| `400`  | `brand_url must be a valid URL`                    | URL failed validation         |
| `400`  | `webhook_url must be a valid URL`                  | Webhook URL failed validation |
| `401`  | `Missing X-API-Key header`                         | No API key provided           |
| `403`  | `You've reached the maximum number of projects...` | Plan project limit reached    |

***

## After bootstrap completes

Once the webhook fires with `"status": "completed"`, your project has data. Use these read-only endpoints (documented in [API reference](/api-reference/introduction)) to retrieve results:

| Endpoint                                        | What it returns                                         |
| ----------------------------------------------- | ------------------------------------------------------- |
| `GET /projects`                                 | List of projects with current brand metrics             |
| `GET /projects/{id}/overview`                   | Per-prompt aggregated analysis with competitor mentions |
| `GET /projects/{id}/visibility?duration_days=7` | Daily visibility, sentiment, and position trends        |
| `GET /projects/{id}/prompts`                    | All tracked prompts with per-provider metrics           |
| `GET /projects/{id}/competitors`                | Detected competitor brands and their metrics            |
| `GET /projects/{id}/sources`                    | Domains cited by AI providers, sorted by citation count |

All data endpoints require the `X-API-Key` header and return responses in the format `{"success": true, "data": {...}}`.

<Tip>
  For a quick summary to present to the user, start with `GET /projects` to get the brand's overall visibility score and sentiment, then `GET /projects/{id}/overview` for a prompt-level breakdown.
</Tip>

***

## Full workflow example

The entire setup can run end-to-end with zero human involvement. The agent generates a temporary email client-side (`temp-{random}@topify.ai`) or uses the user's email — no input required either way.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    # 1. Create an account
    curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/account/create" \
      -H "Content-Type: application/json" \
      -d '{"email": "user@example.com"}'

    # 2. Get an API key (use the password from step 1)
    curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/account/api-key" \
      -H "Content-Type: application/json" \
      -d '{"email": "user@example.com", "password": "Tp..."}'

    # 3. Create a project (use the API key from step 2)
    curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/account/projects" \
      -H "Content-Type: application/json" \
      -H "X-API-Key: tk_live_..." \
      -d '{
        "brand_name": "Acme Corp",
        "brand_url": "https://acme.com",
        "webhook_url": "https://your-service.com/webhooks/topify"
      }'

    # 4. Query project data after webhook confirms completion
    curl "https://topify-customer-api-production.up.railway.app/api/public/v1/projects" \
      -H "X-API-Key: tk_live_..."
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import httpx

    BASE = "https://topify-customer-api-production.up.railway.app/api/public/v1"

    # 1. Create account
    resp = httpx.post(f"{BASE}/account/create", json={
        "email": "user@example.com",
    })
    account = resp.json()["data"]
    password = account["password"]

    # 2. Get API key
    resp = httpx.post(f"{BASE}/account/api-key", json={
        "email": "user@example.com",
        "password": password,
    })
    api_key = resp.json()["data"]["api_key"]

    # 3. Create project
    headers = {"X-API-Key": api_key}
    resp = httpx.post(f"{BASE}/account/projects", json={
        "brand_name": "Acme Corp",
        "brand_url": "https://acme.com",
        "webhook_url": "https://your-service.com/webhooks/topify",
    }, headers=headers)
    project_id = resp.json()["data"]["project_id"]
    print(f"Project {project_id} is bootstrapping...")

    # 4. After webhook confirms completion, query data
    resp = httpx.get(f"{BASE}/projects", headers=headers)
    print(resp.json())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const BASE = "https://topify-customer-api-production.up.railway.app/api/public/v1";

    // 1. Create account
    let resp = await fetch(`${BASE}/account/create`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: "user@example.com" }),
    });
    const { data: account } = await resp.json();

    // 2. Get API key
    resp = await fetch(`${BASE}/account/api-key`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: "user@example.com",
        password: account.password,
      }),
    });
    const { data: keyData } = await resp.json();

    // 3. Create project
    const headers = {
      "Content-Type": "application/json",
      "X-API-Key": keyData.api_key,
    };
    resp = await fetch(`${BASE}/account/projects`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        brand_name: "Acme Corp",
        brand_url: "https://acme.com",
        webhook_url: "https://your-service.com/webhooks/topify",
      }),
    });
    const { data: project } = await resp.json();
    console.log(`Project ${project.project_id} is bootstrapping...`);

    // 4. After webhook confirms completion, query data
    resp = await fetch(`${BASE}/projects`, { headers });
    console.log(await resp.json());
    ```
  </Tab>
</Tabs>
