Errors & Status Codes
Parley uses standard HTTP status codes and returns structured JSON error bodies with machine-readable codes and human-readable messages.
Error Response Format
All error responses follow a consistent structure:
Error body shape
{ "error": { "code": "MACHINE_READABLE_ERROR_CODE", "message": "Human-readable explanation of what went wrong.", "details": {} }}code-- Stable string for programmatic error handlingmessage-- Description suitable for logging or developer debuggingdetails-- Additional context (field-level validation errors, limits, etc.)
HTTP Status Codes
| Code | Meaning | When |
|---|---|---|
| 200 | OK | Request succeeded. Response body contains the requested data. |
| 201 | Created | Resource created successfully (e.g., new webhook subscription). |
| 400 | Bad Request | Invalid or missing parameters. Check the details field for specifics. |
| 401 | Unauthorized | Missing or invalid API key in the X-API-Key header. |
| 403 | Forbidden | Your tier does not have access to this resource (e.g., Free tier accessing webhooks). |
| 404 | Not Found | The requested resource (facility, case, webhook) does not exist. |
| 429 | Too Many Requests | Daily rate limit exceeded. Check rate limit headers for reset time. |
| 500 | Internal Server Error | Something went wrong server-side. Retry the request or open a GitHub issue. |
| 503 | Service Unavailable | API is temporarily unavailable, usually during data ingestion. Retry after a few minutes. |
Error Examples
400 -- Invalid parameters
400Bad Requestapplication/json
{ "error": { "code": "INVALID_PARAMS", "message": "One or more query parameters are invalid.", "details": { "fields": { "state": "Must be a 2-letter state abbreviation", "radius": "Must be between 0 and 50" } } }}401 -- Missing API key
401Unauthorizedapplication/json
{ "error": { "code": "UNAUTHORIZED", "message": "Missing or invalid API key. Include a valid key in the X-API-Key header." }}403 -- Tier restriction
403Forbiddenapplication/json
{ "error": { "code": "TIER_RESTRICTED", "message": "This endpoint requires pro tier or above" }}404 -- Resource not found
404Not Foundapplication/json
{ "error": { "code": "NOT_FOUND", "message": "Facility 'f47ac10b-0000-0000-0000-000000000000' not found" }}429 -- Rate limited
429Too Many Requestsapplication/json
{ "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "You have exceeded your daily rate limit of 5000 requests.", "details": { "limit": 5000, "remaining": 0, "resets_at": "2026-03-21T00:00:00Z" } }}Handling Errors in Code
error-handling.js
async function getFacility(id) { const res = await fetch( `https://api.parley.dev/v1/facilities/${id}`, { headers: { "X-API-Key": process.env.PARLEY_API_KEY } } ); if (!res.ok) { const { error } = await res.json(); switch (res.status) { case 401: throw new Error("Invalid API key"); case 404: return null; // facility not found case 429: // Retry after rate limit resets const resetAt = new Date(error.details.resets_at); const waitMs = resetAt.getTime() - Date.now(); await new Promise(r => setTimeout(r, waitMs)); return getFacility(id); // retry default: throw new Error(error.message); } } const { data } = await res.json(); return data;}Best practices
- •Always check the HTTP status code before parsing the response body.
- •Use the
error.codefield for programmatic branching, not the message text (which may change). - •Log the
meta.request_idfield from successful responses and the full error body from failures for debugging. - •Implement exponential backoff for 429 and 503 responses rather than immediate retries.
- •Monitor the
X-RateLimit-Remainingheader to preemptively slow down before hitting the limit.