Error Handling
The LexBuild API returns structured JSON error responses for all error conditions. Every error follows a consistent format so you can handle them uniformly in your code.
Error Response Format
All errors are returned as JSON with an error object:
{
"error": {
"status": 404,
"code": "DOCUMENT_NOT_FOUND",
"message": "No document found with identifier /us/usc/t1/s999"
}
}
| Field | Type | Description |
|---|---|---|
status | number | HTTP status code |
code | string | Machine-readable error code |
message | string | Human-readable error description |
Rate limit errors include an additional field:
{
"error": {
"status": 429,
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Retry after 23 seconds.",
"retry_after": 23
}
}
HTTP Status Codes
| Status | Meaning | When It Occurs |
|---|---|---|
400 | Bad Request | Invalid query parameters, malformed filters, or validation errors |
404 | Not Found | Document, title, year, or month does not exist |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Unexpected server-side failure |
503 | Service Unavailable | Search service (Meilisearch) is unreachable |
504 | Gateway Timeout | Search request timed out before the backend responded |
Error Codes
| Code | Status | Description |
|---|---|---|
DOCUMENT_NOT_FOUND | 404 | The requested document identifier does not exist |
NOT_FOUND | 404 | No route exists at the requested API path |
REQUEST_ERROR | 400/404/503/504 | General request error (invalid params, missing resource, service unavailable, or timeout) |
RATE_LIMIT_EXCEEDED | 429 | Too many requests in the current window |
INTERNAL_ERROR | 500 | Unexpected server error |
Common Error Scenarios
Document Not Found
Requesting a document with an identifier that does not exist in the database:
curl https://lexbuild.dev/api/usc/documents/t1%2Fs999
HTTP/2 404
{
"error": {
"status": 404,
"code": "DOCUMENT_NOT_FOUND",
"message": "No document found with identifier /us/usc/t1/s999"
}
}
Title or Year Not Found
Requesting a hierarchy entry that does not exist:
curl https://lexbuild.dev/api/usc/titles/99
{
"error": {
"status": 404,
"code": "REQUEST_ERROR",
"message": "No USC title 99 found"
}
}
curl https://lexbuild.dev/api/fr/years/1990
{
"error": {
"status": 404,
"code": "REQUEST_ERROR",
"message": "No FR documents found for year 1990"
}
}
Rate Limit Exceeded
Sending too many requests within the rate limit window:
curl -i https://lexbuild.dev/api/usc/documents/t1%2Fs1
HTTP/2 429
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1743782460
X-RateLimit-Policy: anonymous
{
"error": {
"status": 429,
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Retry after 23 seconds.",
"retry_after": 23
}
}
The Retry-After header and retry_after field both indicate the number of seconds to wait before retrying.
Search Service Unavailable
When the search backend (Meilisearch) is down or unreachable:
curl "https://lexbuild.dev/api/search?q=test"
{
"error": {
"status": 503,
"code": "REQUEST_ERROR",
"message": "Search service is temporarily unavailable"
}
}
This only affects the search endpoint. Document, hierarchy, sources, and stats endpoints operate independently of the search service.
Search Timeout
When the search backend does not respond before the request timeout:
curl "https://lexbuild.dev/api/search?q=test"
{
"error": {
"status": 504,
"code": "REQUEST_ERROR",
"message": "Search request timed out"
}
}
The API intentionally returns a generic timeout message rather than exposing backend details.
Internal Server Error
For unexpected failures, the API returns a generic message without exposing internal details:
{
"error": {
"status": 500,
"code": "INTERNAL_ERROR",
"message": "Internal server error"
}
}
Handling Errors in Code
Here is a pattern for handling API errors in JavaScript:
async function fetchDocument(identifier) {
const response = await fetch(
`https://lexbuild.dev/api/usc/documents/${identifier}`
);
if (!response.ok) {
const body = await response.json();
const error = body.error;
if (error.status === 404) {
console.log(`Document not found: ${error.message}`);
return null;
}
if (error.status === 429) {
const retryAfter = error.retry_after ?? 60;
console.log(`Rate limited. Retrying after ${retryAfter}s`);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return fetchDocument(identifier);
}
throw new Error(`API error ${error.status}: ${error.message}`);
}
return response.json();
}
And in Python:
import requests
import time
def fetch_document(identifier):
url = f"https://lexbuild.dev/api/usc/documents/{identifier}"
response = requests.get(url)
if response.status_code == 404:
print(f"Document not found: {response.json()['error']['message']}")
return None
if response.status_code == 429:
retry_after = response.json()["error"].get("retry_after", 60)
print(f"Rate limited. Retrying after {retry_after}s")
time.sleep(retry_after)
return fetch_document(identifier)
response.raise_for_status()
return response.json()
Tips
- Always check the HTTP status code before parsing the response body. A non-2xx response always contains an
errorobject, never adataobject. - Use the
codefield for programmatic branching, not themessagefield. Messages are human-readable and may change. - For rate limiting, always respect
Retry-After. Do not implement a fixed retry interval โ the header tells you exactly how long to wait. - The search endpoint can return 503 or 504 independently of other endpoints. If search fails, the document and hierarchy endpoints are likely still working.