Error Handling
Understand the error format, HTTP status codes, and how to handle errors gracefully in your application.
Error Response Format
When an API request fails, the response follows the standard envelope with success: false:
{
"success": false,
"payload": null,
"errors": {
"field_name": ["Error message for this field"]
},
"description": "Validation failed"
}
HTTP Status Codes
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded. Response contains the requested data. |
| 201 | Created | Resource was created successfully. |
| 204 | No Content | Request succeeded with no response body (e.g., logout). |
| 400 | Bad Request | The request was malformed or missing required fields. |
| 401 | Unauthorized | Missing or invalid authentication token. |
| 403 | Forbidden | Authenticated but not authorized (e.g., 2FA required). |
| 404 | Not Found | The requested resource does not exist. |
| 422 | Validation Error | Request body failed validation. Check the errors object. |
| 429 | Rate Limited | Too many requests. Wait and retry after Retry-After seconds. |
| 500 | Internal Error | Server error. Contact support if this persists. |
Validation Errors (422)
When request validation fails, the errors field contains a map of field names to arrays of error messages:
{
"success": false,
"payload": null,
"errors": {
"email": ["The email field is required."],
"password": [
"The password field is required.",
"The password must be at least 8 characters."
]
},
"description": "Validation failed"
}
Rate Limiting (429)
When rate limited, the response includes a Retry-After header indicating how many seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
{
"message": "Too Many Attempts."
}
Tip: Implement exponential backoff in your client to handle rate limits gracefully. Check the
X-RateLimit-Remaining header to proactively slow down before hitting the limit.
Handling Errors in Code
# Check the HTTP status code
curl -s -o response.json -w "%{http_code}" \
https://api.proxyhat.com/v1/auth/user \
-H "Authorization: Bearer __API_KEY__"
import requests
response = requests.get(
"https://api.proxyhat.com/v1/auth/user",
headers={"Authorization": "Bearer __API_KEY__"}
)
if response.status_code == 200:
data = response.json()
print(data["payload"])
elif response.status_code == 401:
print("Invalid API key")
elif response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Retry after {retry_after}s")
else:
print(f"Error {response.status_code}: {response.text}")
const response = await fetch("https://api.proxyhat.com/v1/auth/user", {
headers: { "Authorization": "Bearer __API_KEY__" },
});
if (response.ok) {
const data = await response.json();
console.log(data.payload);
} else if (response.status === 401) {
console.error("Invalid API key");
} else if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || 60;
console.error(`Rate limited. Retry after ${retryAfter}s`);
} else {
console.error(`Error ${response.status}`);
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
switch resp.StatusCode {
case 200:
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["payload"])
case 401:
fmt.Println("Invalid API key")
case 429:
retryAfter := resp.Header.Get("Retry-After")
fmt.Printf("Rate limited. Retry after %ss\n", retryAfter)
default:
fmt.Printf("Error %d\n", resp.StatusCode)
}