Operational guide

Handle errors and rate limits

Use stable error codes for application logic, request IDs for support, and idempotency keys whenever you retry a create request.

Error response format

Every 4xx and 5xx response contains an error object. The human-readable message can change; the error code remains stable within the API version.

Error responsejson
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "The project exceeded its request allowance.",
    "request_id": "req_01J6Y44C9N7Q2H5K8M3P"
  }
}
FieldUse
error.codeBranch on this stable, machine-readable value.
error.messageLog or display a concise explanation for a developer.
error.request_idInclude this value when contacting support about the request.

Respond to common errors

StatusCodeWhat to do
400invalid_requestCorrect the field named in the response before retrying.
401authentication_failedCheck the bearer header, key status, and test or live environment.
404delivery_not_foundConfirm the delivery ID and use the key from the project that created it.
409idempotency_conflictReuse an idempotency key only with the original request data.
429rate_limit_exceededWait for the number of seconds in Retry-After.
500–599internal_errorRetry with backoff and the same idempotency key.

Rate limits

Each project can make 600 requests per minute. Successful responses report the limit, remaining requests, and seconds until the current window resets. A 429 response also includes Retry-After.

Rate-limited response headershttp
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 42
Retry-After: 42
Relayline-Request-Id: req_01J6Y44C9N7Q2H5K8M3P

When RateLimit-Remaining approaches zero, reduce concurrency. After a 429, wait for Retry-After before sending another request.

Retry safely

Retry 429 responses, 5xx responses, and network failures. Do not retry other 4xx responses without changing the request. This example handles HTTP retries; add equivalent handling for network errors in your application.

Create with bounded retriesJavaScript
const delay = (milliseconds) =>
  new Promise((resolve) => setTimeout(resolve, milliseconds));

async function createDelivery(payload, apiKey, idempotencyKey) {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(
      'https://api.relayline.example/v1/deliveries',
      {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${apiKey}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ ...payload, idempotency_key: idempotencyKey }),
      },
    );

    if (response.ok) return response.json();

    const canRetry = response.status === 429 || response.status >= 500;
    if (!canRetry || attempt === 3) {
      const problem = await response.json();
      throw new Error(`${problem.error.code}: ${problem.error.message}`);
    }

    const retryAfterHeader = response.headers.get('Retry-After');
    const retryAfter = retryAfterHeader === null
      ? Number.NaN
      : Number(retryAfterHeader);
    const waitSeconds = Number.isFinite(retryAfter) && retryAfter >= 0
      ? retryAfter
      : 2 ** attempt;
    await delay(waitSeconds * 1000);
  }
}