# Error Handling

Error Response Format

All errors return a consistent JSON structure:

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

### HTTP Status Codes

| Status | Meaning                                                   |
| ------ | --------------------------------------------------------- |
| 200    | Success                                                   |
| 400    | Bad Request — invalid parameters or operation failed      |
| 401    | Unauthorized — missing or invalid API key                 |
| 403    | Forbidden — account suspended or insufficient permissions |
| 404    | Not Found — resource does not exist                       |
| 422    | Validation Error — request body failed validation         |
| 429    | Rate Limited — too many requests                          |
| 500    | Server Error — unexpected internal error                  |

### Error Codes Reference

#### Authentication Errors (401)

| Code              | Description                                   |
| ----------------- | --------------------------------------------- |
| `API_KEY_MISSING` | No API key provided in the `X-API-KEY` header |
| `INVALID_API_KEY` | The API key is invalid or has been revoked    |

Example:

```json
{
  "success": false,
  "error": {
    "code": "API_KEY_MISSING",
    "message": "API key is missing"
  }
}
```

#### Authorization Errors (403)

| Code                | Description                                        |
| ------------------- | -------------------------------------------------- |
| `ACCOUNT_SUSPENDED` | Your account has been suspended                    |
| `FORBIDDEN`         | You do not have permission to access this resource |

#### Validation Errors (422)

| Code               | Description                              |
| ------------------ | ---------------------------------------- |
| `VALIDATION_ERROR` | The request body failed validation rules |

Example:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "details": {
      "format": ["The format field is required."],
      "country": ["The selected country is invalid."]
    }
  }
}
```

#### Resource Errors (404)

| Code        | Description                                                              |
| ----------- | ------------------------------------------------------------------------ |
| `NOT_FOUND` | The requested resource does not exist or does not belong to your account |

#### Rate Limit Errors (429)

| Code           | Description                              |
| -------------- | ---------------------------------------- |
| `RATE_LIMITED` | You have exceeded the request rate limit |

Example:

```json
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests.",
    "details": {
      "retry_after": 45
    }
  }
}
```

#### Operation Errors (400/500)

| Code             | Description                         |
| ---------------- | ----------------------------------- |
| `BAD_REQUEST`    | The request could not be processed  |
| `INTERNAL_ERROR` | An unexpected server error occurred |

### Handling Errors in Code

#### Python

```python
import requests

response = requests.get(
    "https://dashboard.plainproxies.com/api/user/profile",
    headers={"X-API-KEY": "YOUR_API_KEY"}
)

if response.status_code == 200:
    data = response.json()
    print(data["data"])
elif response.status_code == 401:
    print("Authentication failed. Check your API key.")
elif response.status_code == 404:
    print("Resource not found.")
elif response.status_code == 429:
    error = response.json()
    retry_after = error.get("error", {}).get("details", {}).get("retry_after", 60)
    print(f"Rate limited. Retry after {retry_after} seconds.")
else:
    error = response.json()
    print(f"Error: {error['error']['message']}")
```

#### JavaScript

```javascript
try {
  const response = await fetch(
    "https://dashboard.plainproxies.com/api/user/profile",
    { headers: { "X-API-KEY": "YOUR_API_KEY" } }
  );

  const data = await response.json();

  if (!data.success) {
    switch (data.error.code) {
      case "INVALID_API_KEY":
        console.error("Check your API key");
        break;
      case "NOT_FOUND":
        console.error("Resource not found");
        break;
      case "RATE_LIMITED":
        const retryAfter = data.error.details?.retry_after || 60;
        console.error(`Rate limited. Retry after ${retryAfter}s`);
        break;
      default:
        console.error(data.error.message);
    }
  }
} catch (error) {
  console.error("Network error:", error);
}
```

### Best Practices

* **Always check `success`** — Don't assume requests succeed
* **Use error codes** — Handle specific codes programmatically, not HTTP status alone
* **Implement retries** — For rate limits (`429`) and transient errors (`500`), retry with exponential backoff
* **Log errors** — Track `error.code` and `error.message` for debugging
* **Show user-friendly messages** — Don't expose raw error codes to end users
* **Respect `retry_after`** — When rate limited, wait the specified number of seconds before retrying


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.plainproxies.com/getting-started/error-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
