Hydra is now in beta|Get started free|Follow our journey on X.com

Error Handling
On this page

Error Handling

The Hydra API uses standard HTTP status codes and returns structured JSON error responses. Every error includes a machine-readable code and a human-readable message.

Error response format

{
	"error": {
		"code": "not_found",
		"message": "Product does not exist."
	}
}
Field Type Description
code string Machine-readable error identifier (see table below).
message string Human-readable explanation.
field string The specific field that caused the error (when applicable).
details array Per-field validation errors (for validation_error only).

Status codes

400 Bad Request

The request is malformed or contains invalid data.

invalid_request — A specific field or parameter is invalid:

{
	"error": {
		"code": "invalid_request",
		"message": "Quantity must be greater than zero.",
		"field": "quantity"
	}
}

validation_error — Multiple fields failed validation:

{
	"error": {
		"code": "validation_error",
		"message": "Request body has invalid fields.",
		"details": [
			{ "field": "title", "message": "Required" },
			{ "field": "price", "message": "Must be a positive integer" }
		]
	}
}

401 Unauthorized

The API key is missing, malformed, or revoked.

{
	"error": {
		"code": "unauthorized",
		"message": "Missing or invalid API key."
	}
}

403 Forbidden

The API key doesn’t have permission for this operation. Common causes:

  • Using a publishable key (pk_*) on a write endpoint
  • Account suspended due to billing issues
{
	"error": {
		"code": "forbidden",
		"message": "Key does not have permission for this action."
	}
}

404 Not Found

The requested resource doesn’t exist, or it belongs to a different project/test mode.

{
	"error": {
		"code": "not_found",
		"message": "Product does not exist."
	}
}

409 Conflict

The request conflicts with the current state. For example, creating a product with a handle that already exists:

{
	"error": {
		"code": "conflict",
		"message": "A product with this handle already exists."
	}
}

429 Too Many Requests

Rate limit or monthly quota exceeded. Check the Retry-After header.

rate_limited — Per-minute rate limit exceeded:

{
	"error": {
		"code": "rate_limited",
		"message": "Too many requests."
	}
}

quota_exceeded — Monthly request quota exhausted:

{
	"error": {
		"code": "quota_exceeded",
		"message": "Monthly API quota exceeded."
	}
}

500 Internal Server Error

An unexpected error occurred on the server. If this persists, contact support.

{
	"error": {
		"code": "internal_error",
		"message": "An unexpected error occurred."
	}
}

Error codes reference

Code Status When it occurs
invalid_request 400 A field value is invalid or missing
validation_error 400 Multiple request body fields failed validation
unauthorized 401 Missing, malformed, or revoked API key
forbidden 403 Insufficient permissions for this operation
account_suspended 403 Account suspended due to failed payment
domain_not_verified 403 Live publishable key requires domain verification
not_found 404 Resource doesn’t exist or is in a different environment
conflict 409 Unique constraint violation (duplicate handle, email, etc.)
rate_limited 429 Per-minute request limit exceeded
quota_exceeded 429 Monthly API request quota exhausted
internal_error 500 Unexpected server error

Handling errors in code

With the SDK

The SDK throws typed errors that you can catch and inspect:

import { Hydra } from '@gethydra/sdk';

const hydra = new Hydra({ secret_key: 'sk_live_...' });

try {
	const product = await hydra.products.get('prod_nonexistent');
} catch (err) {
	if (err.status === 404) {
		console.log('Product not found');
	} else if (err.status === 429) {
		// Back off and retry
		const retryAfter = err.headers?.get('Retry-After');
		console.log(`Rate limited. Retry after ${retryAfter}s`);
	} else {
		throw err; // Re-throw unexpected errors
	}
}

With fetch

const res = await fetch('https://api.hydrajs.dev/v1/products/prod_nonexistent', {
	headers: { Authorization: `Bearer ${SECRET_KEY}` },
});

if (!res.ok) {
	const { error } = await res.json();
	console.error(`${error.code}: ${error.message}`);

	if (error.code === 'rate_limited') {
		const retryAfter = res.headers.get('Retry-After');
		// Wait and retry
	}
}

Idempotency

For POST requests that create resources, you can include an Idempotency-Key header to safely retry failed requests without creating duplicates:

curl -X POST https://api.hydrajs.dev/v1/orders \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Idempotency-Key: unique-request-id-123" \
  -H "Content-Type: application/json" \
  -d '{"checkout_id": "chk_abc123..."}'

If a request with the same idempotency key was already processed, the API returns the original response. Keys expire after 24 hours.

Best practices

  • Check error.code, not error.message. Messages may change; codes are stable.
  • Handle 429 with backoff. Respect the Retry-After header to avoid cascading failures.
  • Use idempotency keys for any POST that creates resources or processes payments.
  • Log the full error response including field and details for debugging validation failures.