Error Handling
How errors are structured, what each error code means, and how to handle them in your integration.
Every error response from the AskEdgar API follows the same JSON structure, regardless of the error type. Once you've built handling for this envelope, you can handle every error the API throws.
Error response format
{
"status": "error",
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded.",
"details": {
"limit": 200,
"window": "1m",
"retry_after": 42
}
},
"request_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
}| Field | Description |
|---|---|
status | Always "error" for error responses, "success" for successful ones |
error.code | Stable, machine-readable string. Safe to match on programmatically |
error.message | Human-readable description. May change over time — don't parse it |
error.details | Additional context specific to the error type. May be {} when no extra info applies |
request_id | UUID for tracing. Also returned as the X-Request-ID response header. Include this when contacting support |
Error code reference
Authentication errors (401)
| Code | Meaning | What to do |
|---|---|---|
missing_api_key | No API-KEY header in the request | Add the API-KEY header. Note: it's not Authorization or X-API-Key |
invalid_api_key | Key not recognized after lookup | Check for typos, extra whitespace, or a missing ask-live- prefix |
api_key_disabled | Key exists but has been disabled | Check the API Platform to re-enable it, or generate a new key |
Authorization errors (403)
| Code | Meaning | What to do |
|---|---|---|
endpoint_not_allowed | Your organization or key doesn't have access to this endpoint | Check your plan's endpoint access. Some endpoints are Enterprise-only. Contact us to upgrade |
Payment errors (402)
| Code | Meaning | What to do |
|---|---|---|
insufficient_credits | Not enough credits to process the request | Add credits via the billing page. The response includes your current balance and the estimated cost |
{
"status": "error",
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits to process this request.",
"details": {
"balance_dollars": "$0.50",
"estimated_cost_dollars": "$1.20",
"unit": "dollars"
}
}
}You're never charged for a failed request. Use the free cost estimate endpoint to check costs before making a call.
Validation errors (422)
| Code | Meaning | What to do |
|---|---|---|
validation_error | One or more query parameters failed validation | Check the details.fields array for which parameters are invalid and why |
attribute_error | Attribute error during request processing | Review your request parameters against the API reference |
{
"status": "error",
"error": {
"code": "validation_error",
"message": "Request validation failed.",
"details": {
"fields": [
{
"field": "ticker",
"reason": "Value error, Field 'ticker' must contain only uppercase letters, numbers, dots, hyphens, or carets."
}
]
}
}
}Common validation issues: ticker must be uppercase (AAPL not aapl), dates must be YYYY-MM-DD format, and numeric range filters must have min ≤ max.
Rate limit errors (429)
| Code | Meaning | What to do |
|---|---|---|
rate_limit_exceeded | Too many requests in the current 1-minute window | Wait the number of seconds in the Retry-After header, then retry |
trial_ticker_limit | Trial account exceeded 50 unique tickers per endpoint per day | Wait until midnight Central Time for the counter to reset, or upgrade your plan |
See the rate limits page for full details on limits by plan, response headers, and retry strategies.
Server errors (500, 503)
| Code | Meaning | What to do |
|---|---|---|
internal_error | Unhandled server error | Retry after a short delay. If persistent, contact support with the request_id |
upstream_error | An upstream dependency failed | Retry after a short delay |
database_error | Database error occurred | Retry after a short delay |
service_unavailable | Infrastructure temporarily unavailable | Retry after the number of seconds in the Retry-After header (typically 5s) |
Server errors are rare and almost always transient. A simple retry with a short delay will resolve most of them.
Not found (404)
| Code | Meaning | What to do |
|---|---|---|
not_found | The requested resource doesn't exist | Verify the endpoint path. Check for typos or removed endpoints in the changelog |
Error priority
Errors are checked in a specific order. If multiple issues exist, you'll only see the first one that fails:
- Authentication (401) → 2. Authorization (403) → 3. Rate limits (429) → 4. Credits (402) → 5. Validation (422) → 6. Processing
This means if you're seeing a 429, your authentication is fine. If you're seeing a 402, you're authenticated, authorized, and within your rate limit — you just need more credits.
Handling errors in code
Here's a robust error handler that covers every category:
import time
import requests
API_KEY = "your_api_key"
BASE_URL = "https://api.askedgar.com"
HEADERS = {"API-KEY": API_KEY}
class AskEdgarError(Exception):
def __init__(self, status_code, code, message, details=None, request_id=None):
self.status_code = status_code
self.code = code
self.message = message
self.details = details or {}
self.request_id = request_id
super().__init__(f"[{status_code}] {code}: {message}")
def api_request(endpoint, params, max_retries=3):
for attempt in range(max_retries):
resp = requests.get(f"{BASE_URL}{endpoint}", params=params, headers=HEADERS)
# Success
if resp.status_code == 200:
return resp.json()
# Parse the error envelope
try:
body = resp.json()
error = body.get("error", {})
code = error.get("code", "unknown")
message = error.get("message", "Unknown error")
details = error.get("details", {})
request_id = body.get("request_id")
except ValueError:
code, message, details, request_id = "unknown", resp.text, {}, None
# Retryable: rate limit
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 2 ** attempt))
print(f"Rate limited. Retrying in {wait}s... (attempt {attempt + 1})")
time.sleep(wait)
continue
# Retryable: server errors
if resp.status_code in (500, 503):
wait = int(resp.headers.get("Retry-After", 5))
print(f"Server error ({code}). Retrying in {wait}s... (attempt {attempt + 1})")
time.sleep(wait)
continue
# Non-retryable: raise immediately
raise AskEdgarError(resp.status_code, code, message, details, request_id)
raise AskEdgarError(429, "max_retries", f"Max retries ({max_retries}) exceeded")Usage:
try:
data = api_request("/v1/dilution-rating", {"ticker": "AAPL"})
print(data["results"])
except AskEdgarError as e:
if e.code == "insufficient_credits":
print(f"Need more credits. Balance: {e.details.get('balance_dollars')}")
elif e.code == "endpoint_not_allowed":
print("This endpoint requires an Enterprise plan.")
elif e.code == "validation_error":
for field in e.details.get("fields", []):
print(f"Invalid field '{field['field']}': {field['reason']}")
else:
print(f"API error: {e}")
print(f"Request ID for support: {e.request_id}")Best practices
- Match on
error.code, noterror.message. Codes are stable and machine-readable. Messages may change. - Always capture
request_id. It's the fastest way for support to find your request in our logs. - Retry 429s and 5xx errors. These are transient. Use the
Retry-Afterheader when available, exponential backoff otherwise. - Don't retry 401, 402, 403, or 422 errors. These require you to fix something (auth, credits, permissions, or parameters) before the request can succeed.
- Use the cost estimate endpoint to avoid
insufficient_creditserrors entirely by checking costs upfront.
Next steps
Updated about 3 hours ago