Concepts

Rate limits

Limits keep the platform fast for everyone and protect your workspace from a runaway script ringing hundreds of people. Every limit answers 429 with a clear code.

The limits

LimitDefaultError code
Requests per workspaceA rolling per-minute budget shared by all your keysrate_limited
Outbound calls per minute10 per workspacecall_rate_limited
Outbound calls per day500 per workspacecall_daily_limit
Plan quotaThe calls included in your planquota_exceeded

All of them answer 429 with type: rate_limit_error.

Request rate

Requests are counted per workspace, across all of its keys. When the budget is spent the API answers 429 with a Retry-After header: the number of seconds to wait. Normal integrations — syncing calls into a CRM, starting campaigns — stay far below it.

Outbound calls

POST /calls has its own, tighter limits, because each request rings a real phone: by default 10 calls a minute and 500 a day per workspace. Space out large batches instead of sending them at once.

Need more?

The defaults suit most workspaces. If you run larger campaigns, contact us at kontakt@voximate.no and we will raise them.

Plan quota

When your plan’s calls are used up, starting a call answers 429 quota_exceeded. Reading data keeps working. Upgrade the plan in the dashboard under Billing.

Backing off

  • Honour Retry-After when it is present.
  • Otherwise wait 0.5 s, then 1 s, 2 s, 4 s … with a little random jitter, and give up after a few attempts.
  • Send an Idempotency-Key on every POST you might retry.
voximate.js
// Retry 429 and 5xx with exponential backoff; never retry other 4xx.
async function voximate(path, init = {}, attempt = 0) {
  const res = await fetch(`https://api.voximate.no/public/v1${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.VOXIMATE_API_KEY}`,
      'Content-Type': 'application/json',
      ...init.headers,
    },
  });
  const retryable = res.status === 429 || res.status >= 500;
  if (retryable && attempt < 4) {
    const retryAfter = Number(res.headers.get('Retry-After'));
    const wait = Number.isFinite(retryAfter) && retryAfter > 0
      ? retryAfter * 1000
      : Math.min(30_000, 500 * 2 ** attempt) + Math.random() * 250;
    await new Promise((r) => setTimeout(r, wait));
    return voximate(path, init, attempt + 1);
  }
  const body = await res.json();
  if (!res.ok) {
    const err = new Error(body.error.message);
    Object.assign(err, { type: body.error.type, code: body.error.code, requestId: body.request_id });
    throw err;
  }
  return body;
}