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

# API keys

> Create and manage API keys to authenticate with the Quikly REST API from external tools, scripts, and automations.

API keys let you access the Quikly REST API programmatically. Use them with n8n, custom scripts, CI/CD pipelines, or any HTTP client. The same kind of key authenticates the **Discovery Copilot** browser extension — see [Discovery Copilot](/guides/discovery-copilot-extension).

## Create an API key

<Steps>
  <Step title="Open API key settings">
    Go to **Settings → API Keys** in the Quikly app.
  </Step>

  <Step title="Click Create API Key">
    Enter a descriptive name (e.g., "n8n automation" or "Cursor MCP") and select the scopes you need.
  </Step>

  <Step title="Copy the key">
    The full key is shown **only once**. Copy it and store it somewhere safe — you cannot retrieve it later.
  </Step>
</Steps>

## Scopes

Each API key has one or more scopes that control what it can do:

| Scope        | Access level | Example operations                                            |
| ------------ | ------------ | ------------------------------------------------------------- |
| `read`       | Read-only    | List proposals, get proposal details, AI rate recommendations |
| `write`      | Write access | Create proposals, share proposals, send messages              |
| `read,write` | Full access  | All operations (default)                                      |

<Tip>
  Follow the principle of least privilege. If an integration only needs to read data, create a key with `read` scope only.
</Tip>

## Authenticate requests

Include your API key in the `X-API-Key` header on every request:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.getquikly.com/api/external/v1/me" \
    -H "X-API-Key: qk_your_api_key_here"
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.getquikly.com/api/external/v1/me", {
    headers: {
      "X-API-Key": "qk_your_api_key_here",
    },
  });
  const user = await response.json();
  console.log(user);
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.getquikly.com/api/external/v1/me",
      headers={"X-API-Key": "qk_your_api_key_here"},
  )
  print(response.json())
  ```
</CodeGroup>

## Rate limits and quota

API calls are rate-limited per minute and capped monthly depending on your plan:

| Plan         | Monthly quota | Per-minute limit |
| ------------ | ------------- | ---------------- |
| Starter      | 15 calls      | 10/min           |
| Professional | 2,000 calls   | 30/min           |
| Agency       | 2,000 calls   | 60/min           |
| Lifetime     | 5,000 calls   | 60/min           |

When you exceed your monthly quota, the API returns `402 Payment Required`.
When you exceed your per-minute limit, the API returns `429 Too Many Requests` and includes a `Retry-After` header.

### Per end-user limits (`X-External-User-Id`)

When `X-External-User-Id` is present (after normalization), the API applies **additional** limits on top of your account totals:

* **Per end-user per minute:** default 12 requests/minute per distinct `X-External-User-Id` (configurable server-side).
* **Per end-user per UTC calendar month:** default `min(your plan's monthly API quota, 250)` calls for that same `X-External-User-Id`.

If the end-user slice is exceeded, responses use `402` with `error: external_user_quota_exceeded` or `429` with `error: external_user_rate_limit_exceeded`.

These limits use in-memory counters (they reset on server restart and on UTC month rollover for the monthly slice). They mitigate one downstream tenant burning an entire API key's pool.

### Per agent/workflow limits (`X-Agent-Id`)

When `X-Agent-Id` is present (after normalization), the same pattern applies **independently** of `X-External-User-Id`:

* **Per agent per minute:** default 12 requests/minute (configurable server-side).
* **Per agent per UTC calendar month:** default `min(your plan's monthly API quota, 250)` calls for that `X-Agent-Id`.

Errors: `402` with `error: agent_id_quota_exceeded` or `429` with `error: agent_id_rate_limit_exceeded`.

If both `X-External-User-Id` and `X-Agent-Id` are set, **all three** limits apply: account-wide, per end-user, and per agent.

## Integration traceability headers

For external integrations, send these headers on every request:

| Header               | Required | Description                                                          |
| -------------------- | -------- | -------------------------------------------------------------------- |
| `X-Partner-Id`       | Yes      | Integration owner identifier (your workspace, app, or company slug). |
| `X-External-User-Id` | Yes      | End-user identifier in your system.                                  |
| `X-Agent-Id`         | Yes      | Agent or workflow identifier that triggered the request.             |

Requests without these headers can be rejected with `400 Bad Request`.

<Warning>
  Self-serve API keys are for your own direct integrations. Multi-tenant resale or proxying Quikly as a hidden backend for third-party customers is restricted and can trigger key suspension. See [Self-serve vs Partner tier](/integrations/pricing-and-tiers) for the right tier for your use case.
</Warning>

Check your current usage with:

```bash theme={null}
curl -H "X-API-Key: qk_your_api_key_here" \
  "https://api.getquikly.com/api/external/v1/usage"
```

You can also pre-check before an operation:

```bash theme={null}
curl -H "X-API-Key: qk_your_api_key_here" \
  "https://api.getquikly.com/api/external/v1/usage/precheck?operation=proposal_create"
```

## Response headers

Every response from the public API includes these attribution headers. Clients can safely ignore them, but they are useful for logging, debugging, and proving that a given piece of data came from Quikly:

| Header                  | Description                                                               |
| ----------------------- | ------------------------------------------------------------------------- |
| `X-Quikly-Source`       | Always `quikly.ai`. Identifies the response as generated by Quikly's API. |
| `X-Quikly-Endpoint`     | The exact endpoint path that produced the response.                       |
| `X-Quikly-Generated-At` | ISO-8601 UTC timestamp of when the response was generated.                |
| `X-Quikly-Api-Version`  | API version that served the request (currently `v1`).                     |

<Note>
  Removing or masking these headers before relaying Quikly-generated content to end-users is a breach of the [Terms of Service §5.3.2](https://getquikly.com/legal/terms) on self-serve plans. Partner integrations can negotiate attribution rules in their agreement.
</Note>

## Manage existing keys

In **Settings → API Keys**, you can:

* **Rename** a key to keep your list organized
* **Deactivate** a key temporarily without deleting it
* **Revoke** a key permanently — revoked keys cannot be reactivated

## Security best practices

<Warning>
  Never commit API keys to version control or expose them in client-side code.
</Warning>

* **Use environment variables** to store keys in your deployment environment.
* **Rotate keys periodically** — create a new key, update your integrations, then revoke the old one.
* **Use the narrowest scope** that your integration requires.
* **Set expiration** when creating keys for temporary integrations. You can specify `expires_in_days` during creation.
* **Monitor usage** by checking the usage endpoint or the API Keys page in the dashboard. Each key shows its last-used timestamp.
* **Revoke immediately** if a key is compromised.

## Key format

Quikly API keys follow the format `qk_` followed by a random string. When you view keys in the dashboard, only the prefix (e.g., `qk_abc1...`) is visible — the full key is only shown at creation time.
