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

# Scan & Settings

> Trigger an on-demand AI Visibility scan and configure which models to use, scan cadence, and feature toggles

This page covers two related groups of endpoints:

* **`POST /scan`** — kick off an immediate re-scan (useful right after a metadata change or when you want fresh data without waiting for the daily run).
* **`GET / PUT /settings`** — choose which AI models to scan, how often the scheduled scans run, and whether AI Visibility is enabled at all for the app.

***

## Trigger an on-demand scan

```
POST /v1/ai-visibility/:appId/scan
```

Sends every active prompt for the app to every enabled AI model, parses the answers, resolves competitor app names, and updates the daily roll-up. Returns immediately (HTTP 201); the actual work runs in the background and full results land in `GET /overview` within roughly **3–6 minutes**.

> Scheduled scans run automatically — you should only need this endpoint when you want fresh data *now* (after a launch, a new feature, a metadata change, or right after adding new intents/prompts).

### Path parameters

| Name  | Type   | Required | Description            |
| ----- | ------ | -------- | ---------------------- |
| appId | string | Yes      | Apple App ID (numeric) |

### Body

No body is required. The scan uses the app's saved settings (country, models, language).

### Response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "taskRunId": "run_x9z2...",
    "promptsTotal": 48,
    "models": ["chatgpt", "claude", "gemini", "perplexity"],
    "estimatedCost": 1536
  }
}
```

| Field           | Description                                                                                                                                                  |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `taskRunId`     | Trigger.dev run id for the orchestrator. Useful for support requests but not normally needed.                                                                |
| `promptsTotal`  | Number of active prompts that will be sent.                                                                                                                  |
| `models`        | The models that will receive each prompt — derived from your settings, capped at your plan's `maxModels` and filtered to providers Appeeky has API keys for. |
| `estimatedCost` | AI Visibility credits this scan will consume. Same value as the `X-Credit-Cost` header.                                                                      |

If there are **no active prompts** the call returns `400 NO_PROMPTS`. Run [bootstrap](/docs/ai-visibility-bootstrap) first or add intents + prompts manually.

### Same-day idempotency

Each `(prompt × model × day)` is fingerprinted before the model call. Re-running `/scan` later the same day **won't** charge you for a second round of model calls — the existing answers are reused. So you can hit "Refresh" in the UI without worrying about double-billing.

If you really want a re-query on the same day (e.g. you suspect a flaky earlier answer), pause the prompt, scan, then re-activate it; or wait until tomorrow's automatic scan.

***

## Get or update settings

### `GET /v1/ai-visibility/:appId/settings`

Returns the current scanning configuration for the app, plus the list of AI providers Appeeky currently has credentials for (so you can render a UI that only offers configurable options).

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "settings": {
      "ownerEmail": "you@example.com",
      "appId": "1234567890",
      "country": "us",
      "language": "en",
      "enabled": true,
      "models": ["chatgpt", "perplexity"],
      "scanCadenceDays": 3,
      "nextScanAt": "2026-05-15T08:00:00.000Z",
      "lastScanAt": "2026-05-12T08:01:14.221Z",
      "lastBootstrapAt": "2026-05-10T15:42:08.001Z"
    },
    "availableModels": ["chatgpt", "claude", "gemini", "perplexity"],
    "tier": {
      "planId": "small",
      "enabled": true,
      "maxModels": 2,
      "maxApps": 2,
      "maxCountries": 1
    },
    "aiVisibilityCredits": {
      "limit": 8000,
      "used": 1944,
      "remaining": 6056,
      "estimatedMonthlyCost": 5760,
      "resetDate": "2026-06-12T00:00:00.000Z"
    }
  }
}
```

`settings` is `null` until you've called [bootstrap](/docs/ai-visibility-bootstrap) at least once.

`tier` reflects the caller's current plan and the per-plan AI Visibility caps (see [pricing overview](/docs/ai-visibility-overview#pricing)). Use it in your dashboard to grey out model toggles the user can't actually enable: e.g. on the `small` plan above, only the first 2 entries in `availableModels` are selectable.

`aiVisibilityCredits` is the dedicated credit bucket for AI Visibility (separate from your general API quota). `estimatedMonthlyCost` is a forecast based on the current cadence and enabled models — `(prompts × Σ(model_cost)) × (30 / scanCadenceDays)`. Use it to surface "you'll spend \~5,760 / 8,000 this month" before the user commits to a setting change.

### `PUT /v1/ai-visibility/:appId/settings`

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "enabled": true,
  "models": ["chatgpt", "perplexity"],
  "scanCadenceDays": 1,
  "language": "en"
}
```

| Field             | Type      | Description                                                                                                                                                                                                                                                                                      |
| ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `enabled`         | bool      | Master switch. `false` stops scheduled scans (existing data and intents stay).                                                                                                                                                                                                                   |
| `models`          | string\[] | Which AI models to scan. Subset of `availableModels`. The `chatgpt` model is the most commonly enabled — most users start with one or two and expand later.                                                                                                                                      |
| `scanCadenceDays` | int       | `0` disables scheduled scans, `1` = daily, `3` = every 3 days **(default)**, `7` = weekly, max `30`. The next scan after a settings change is scheduled `scanCadenceDays` from now. AI recommendations change slowly, so 3-day cadence captures most movement at a third of daily's credit cost. |
| `language`        | string    | ISO 639-1, biases newly-generated prompts. Doesn't affect existing prompts.                                                                                                                                                                                                                      |

All fields are optional — only the ones you provide are updated.

***

## Cost controls

Both manual and scheduled scans bill against the **dedicated AI Visibility credit bucket** (`api_plans.ai_visibility_monthly_credits`) — *not* your general API quota. Each call costs **per (prompt × model)**, summed across every model you've enabled.

| Model        | Credits per call |
| ------------ | ---------------- |
| `chatgpt`    | 9                |
| `claude`     | 15               |
| `gemini`     | 5                |
| `perplexity` | 3                |

A typical setup of **8 intents × 6 prompts = 48 prompts** at the **default 3-day cadence** (10 scans/month) burns:

| Models enabled           | Credits per scan      | Monthly (3-day cadence) | Monthly (daily cadence) |
| ------------------------ | --------------------- | ----------------------- | ----------------------- |
| `chatgpt` only           | `48 × 9` = **432**    | \~4,300                 | \~13,000                |
| `chatgpt` + `perplexity` | `48 × 12` = **576**   | \~5,800                 | \~17,300                |
| All four                 | `48 × 32` = **1,536** | \~15,400                | \~46,100                |

The exact estimate for your current setup is returned by `GET /settings → aiVisibilityCredits.estimatedMonthlyCost`. Manual `/scan` calls return the per-call charge in `X-Credit-Cost` (and the response body's `estimatedCost`).

### What happens when the bucket runs low

| Trigger                 | Behavior                                                                                                                                                                                                                                                                                                                                                                                                   |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Manual `/scan`**      | **Hard fail** — the API returns `429 AI_VISIBILITY_QUOTA_EXCEEDED` with the requested cost and your remaining balance. No scan runs, no credits deducted.                                                                                                                                                                                                                                                  |
| **Scheduled cron scan** | **Graceful degrade** — the orchestrator drops models in cost-descending order (`claude → gemini → perplexity`), keeping `chatgpt` as the most user-valuable fallback, until the scan fits remaining budget. The dropped models are recorded on the run row (`models_dropped`, `degraded_reason`). When even `chatgpt`-only doesn't fit, the scan is skipped entirely and `next_scan_at` is pushed out 24h. |

You can detect a degraded scan in the dashboard by checking `ai_visibility_runs.models_dropped` — it's empty when the full requested set ran, otherwise it lists the silently-removed models so you can prompt the user to upgrade.

### Tier limits on `models[]`

The PUT endpoint enforces a per-plan cap on how many models you can enable simultaneously:

| Plan       | Monthly AI Visibility credits | Allowed models                |
| ---------- | ----------------------------: | ----------------------------- |
| Free       |                             0 | — (AI Visibility unavailable) |
| Indie      |                         3,000 | 1                             |
| Starter    |                         8,000 | 2                             |
| Growth     |                        20,000 | 4 (all)                       |
| Pro        |                        50,000 | 4 (all)                       |
| Enterprise |                       150,000 | 4 (all)                       |

If you submit `models: ["chatgpt", "claude"]` on the Indie plan, the API returns `403 TIER_LIMIT_EXCEEDED` and your settings stay unchanged.

A practical recommendation:

* **Start with `["chatgpt"]`.** It's the dominant assistant by usage and gives you the most signal per credit.
* **Add `perplexity`** when you want web-grounded comparisons (Perplexity returns citations alongside the answer — useful for understanding *why* the assistant picked an app).
* **Add `claude` and `gemini`** when you have evidence your audience uses them. Note that `claude` is the most expensive at 15 credits/call — the scheduler will drop it first when budget gets tight.

***

## Code examples

<CodeGroup>
  ```bash curl theme={"theme":{"light":"github-light","dark":"github-dark"}}
  # Manual scan
  curl -X POST "https://api.appeeky.com/v1/ai-visibility/1234567890/scan" \
    -H "X-API-Key: YOUR_API_KEY"

  # Read settings
  curl "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us" \
    -H "X-API-Key: YOUR_API_KEY"

  # Switch to weekly scans on chatgpt + perplexity
  curl -X PUT "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us" \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "models": ["chatgpt", "perplexity"], "scanCadenceDays": 7 }'

  # Pause AI Visibility for this app
  curl -X PUT "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us" \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "enabled": false }'
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const HEADERS = { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" };

  // Trigger an immediate scan after a metadata update
  await fetch(
    "https://api.appeeky.com/v1/ai-visibility/1234567890/scan",
    { method: "POST", headers: HEADERS }
  );

  // Switch the app from daily to weekly scans
  await fetch(
    "https://api.appeeky.com/v1/ai-visibility/1234567890/settings?country=us",
    {
      method: "PUT",
      headers: HEADERS,
      body: JSON.stringify({ scanCadenceDays: 7 }),
    }
  );
  ```
</CodeGroup>

***

## Credits

| Endpoint        | Cost                                                                           | Bucket        |
| --------------- | ------------------------------------------------------------------------------ | ------------- |
| `POST /scan`    | Dynamic — `Σ(model credit cost) × prompts_count`. Returned in `X-Credit-Cost`. | AI Visibility |
| `GET /settings` | 1 credit                                                                       | AI Visibility |
| `PUT /settings` | Free                                                                           | —             |

All AI Visibility endpoints meter against `api_plans.ai_visibility_monthly_credits`. The middleware sets `X-AiVisibility-Limit` and `X-AiVisibility-Remaining` headers on the response so dashboards can render the current bucket state.

***

## Errors

| Status | Code                            | When                                                                                                        |
| ------ | ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 400    | NO\_PROMPTS                     | App has no active prompts — run [bootstrap](/docs/ai-visibility-bootstrap) first                            |
| 400    | INVALID\_APP\_ID                | Missing or non-numeric app ID                                                                               |
| 403    | TIER\_LIMIT\_EXCEEDED           | `models[]` exceeds your plan's max, or AI Visibility is unavailable on your plan                            |
| 401    | —                               | Missing or invalid API key / JWT                                                                            |
| 429    | AI\_VISIBILITY\_QUOTA\_EXCEEDED | Insufficient AI Visibility credits — top up, downgrade your model selection, or wait for the quota to reset |

***

## See also

* [Bootstrap](/docs/ai-visibility-bootstrap) — required before the first scan
* [Overview endpoint](/docs/ai-visibility-overview-endpoint) — read fresh data after the scan completes
* [Intents](/docs/ai-visibility-intents) — change what's scanned
* [Prompts](/docs/ai-visibility-prompts) — change which queries are sent
