# Errors

All API errors return a consistent JSON format with a machine-readable error code and human-readable message.

## Error response format

```json
{
  "error": "Human-readable error message",
  "code": "ERROR_CODE"
}
```

Some errors include additional fields:

```json
{
  "error": "Usage limit exceeded",
  "code": "USAGE_LIMIT_EXCEEDED",
  "usage": { "used": 5000, "limit": 5000 },
  "upgradeUrl": "https://app.promptlycms.com/settings?upgrade"
}
```

| Field | Type | Always present | Description |
|-------|------|----------------|-------------|
| `error` | `string` | Yes | Human-readable error message |
| `code` | `string` | Yes | Machine-readable error code |
| `usage` | `object` | No | Usage data (only on `429` responses) |
| `upgradeUrl` | `string` | No | Plan upgrade link (only on `429` responses) |

## Error codes

### 401 - Unauthorized

```json
{
  "error": "Missing or invalid API key",
  "code": "UNAUTHORIZED"
}
```

**Cause:** The `Authorization` header is missing or empty.

**Solution:** Include a valid Bearer token: `Authorization: Bearer pk_live_...`
```json
{
  "error": "API key is invalid or has been revoked",
  "code": "INVALID_KEY"
}
```

**Cause:** The API key does not exist or has been revoked.

**Solution:** Check your key in the [dashboard](https://app.promptlycms.com/settings?apiKeys). Generate a new key if needed.
**Note:** The SDK also throws `UNAUTHORIZED` as a client-side error if no API key is provided to `createPromptlyClient()` and `PROMPTLY_API_KEY` is not set in the environment. See the [SDK error handling guide](https://docs.promptlycms.com/guides/error-handling/).

### 404 - Not Found

```json
{
  "error": "Prompt not found",
  "code": "NOT_FOUND"
}
```

**Cause:** The prompt ID does not exist or the prompt has not been published.

**Solution:** Check the prompt ID in your CMS dashboard. Make sure the prompt is published - drafts are not accessible via the API.
```json
{
  "error": "Version not found",
  "code": "VERSION_NOT_FOUND"
}
```

**Cause:** The requested version does not exist for this prompt.

**Solution:** Omit the `version` parameter to get the latest version, or check available versions with `GET /prompts?include_versions=true`.
### 400 - Bad Request

```json
{
  "error": "Invalid request",
  "code": "BAD_REQUEST"
}
```

**Cause:** The request is malformed (e.g. invalid prompt ID format).

**Solution:** Check that your request URL and parameters are correctly formatted.

### 429 - Usage Limit Exceeded

```json
{
  "error": "Usage limit exceeded",
  "code": "USAGE_LIMIT_EXCEEDED",
  "usage": { "used": 5000, "limit": 5000 },
  "upgradeUrl": "https://app.promptlycms.com/settings?upgrade"
}
```

**Cause:** You have exceeded your monthly request quota.

**Solution:** Upgrade your plan at the provided `upgradeUrl`, or wait for your quota to reset. See [Rate Limits](https://docs.promptlycms.com/api/rate-limits/) for details on plan tiers and best practices.

### 405 - Method Not Allowed

```json
{
  "error": "Method not allowed",
  "code": "METHOD_NOT_ALLOWED"
}
```

**Cause:** You used an HTTP method other than `GET` or `OPTIONS`.

**Solution:** The API is read-only. Use `GET` for all requests.

### 500 - Internal Error

```json
{
  "error": "Internal server error",
  "code": "INTERNAL_ERROR"
}
```

**Cause:** An unexpected error occurred on the server.

**Solution:** Retry the request. If the error persists, contact support.

## Error handling examples

```typescript
const response = await fetch(
  `https://api.promptlycms.com/prompts/${promptId}`,
  {
    headers: { Authorization: `Bearer ${apiKey}` },
  },
);

if (!response.ok) {
  const body = await response.json();

  if (body.code === 'NOT_FOUND') {
    console.error(`Prompt "${promptId}" not found`);
    return null;
  }

  if (body.code === 'USAGE_LIMIT_EXCEEDED') {
    console.error('Rate limited:', body.usage);
    console.error('Upgrade at:', body.upgradeUrl);
    return null;
  }

  throw new Error(`API error: ${body.code} - ${body.error}`);
}

const prompt = await response.json();
```
```python
import requests

response = requests.get(
    f"https://api.promptlycms.com/prompts/{prompt_id}",
    headers={"Authorization": f"Bearer {api_key}"},
)

if response.status_code != 200:
    body = response.json()

    if body["code"] == "NOT_FOUND":
        print(f"Prompt '{prompt_id}' not found")
    elif body["code"] == "USAGE_LIMIT_EXCEEDED":
        print(f"Rate limited: {body['usage']}")
        print(f"Upgrade at: {body['upgradeUrl']}")
    else:
        raise Exception(f"API error: {body['code']} - {body['error']}")
else:
    prompt = response.json()
```
## Quick reference

| Code | HTTP Status | Issue | Solution |
|------|-------------|-------|----------|
| `UNAUTHORIZED` | 401 | Missing API key | Add `Authorization: Bearer pk_live_...` header |
| `INVALID_KEY` | 401 | Invalid or revoked key | Check or regenerate key in dashboard |
| `NOT_FOUND` | 404 | Prompt does not exist | Verify prompt ID and publish status |
| `VERSION_NOT_FOUND` | 404 | Version does not exist | Omit version or check available versions |
| `BAD_REQUEST` | 400 | Malformed request | Check URL and parameter format |
| `USAGE_LIMIT_EXCEEDED` | 429 | Monthly quota exceeded | Upgrade plan or wait for reset |
| `METHOD_NOT_ALLOWED` | 405 | Wrong HTTP method | Use `GET` requests only |
| `INTERNAL_ERROR` | 500 | Server error | Retry the request |