SwooshConnect

Limits, errors and polling

Exact rate limits, body caps, import caps, the error envelope, pagination, and how to poll because there are no webhooks.

Made by SwooshConnect
On this page

Rate limit

  • 60 requests per minute per API key, on REST and MCP alike. The window slides.
  • Before a key is read, each IP address is limited to 10 requests per 10 seconds. A flood without a valid key hits this first.
  • Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Reset is a timestamp in milliseconds since the epoch.
  • A blocked request answers HTTP 429. It usually carries Retry-After in whole seconds. Do not rely on that header being present.
json
{ "error": "rate_limit_exceeded" }

The 429 body from the rate limiter is this short form. The MCP endpoint answers in JSON-RPC form with the same 429 status. A rate_limited error from a platform uses the normal envelope below.

Body size caps

DoorCapOver the cap
REST2,500,000 bytes (2.5 MB)413, code payload_too_large
MCP (https://swooshrank.com/api/mcp)1,048,576 bytes (1 MiB)413, code payload_too_large

Import caps

ToolCap
dialer_import_contacts, csv20,000 contacts per call. The csv text is at most 2,000,000 characters.
dialer_import_contacts, rows5,000 rows per call. Send either csv or rows, never both.

To import more than 5,000 rows as JSON, split them into several calls. The REST body cap still applies to each call.

Error envelope

Every failure from a tool answers JSON with one stable code and a readable message. A code may add extra fields, for example a reason.

json
{ "error": { "code": "not_found", "message": "No campaign with that id." } }
CodeHTTPMeaning
unauthorized401The API key is missing, invalid, or revoked.
invalid_input400The request body or parameters failed validation. The issues field lists each problem.
not_found404The account, review, post, campaign, contact or agent referenced doesn't exist for your organization.
not_supported400The action isn't available on this platform.
payment_required402A payment method is needed, or paid accounts are paused until a renewal payment clears. The reason field says which.
provider_auth401The platform no longer accepts the stored authorization. Reconnect the account.
provider_permission403The connected account lacks a permission this action needs on the platform.
rate_limited429Too many requests; retry after the interval in the response.
provider_error502The platform returned an error.
transient503A temporary failure on the platform's side. Retry.
payload_too_large413The request body is over the cap above.

Pagination

list_reviews and inbox_list take an optional cursor and return nextCursor. Pass the returned nextCursor back as cursor for the next page. Stop when it is null or missing. customers_list takes max (1 to 100) and returns nextCursor but has no cursor input. calls_log uses since instead (next section).

No webhooks. Poll.

SwooshConnect does not send webhooks. To learn about new calls, poll calls_log. A call appears once it has ended and has been over for about five minutes, so its outcome is final. Each call arrives exactly once, oldest first.

  • First poll: set since to the current time as an ISO 8601 date-time. Store it.
  • Call calls_log with since set to your stored value. Use limit up to 200. The default is 50.
  • Handle every call in calls.
  • If nextCursor is a string, store it as the new since. If it is null, nothing new arrived. Keep your old value.
  • Sleep at least a minute, then repeat from step 2. If a page was full, repeat at once.
bash
SINCE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

curl -G -H "Authorization: Bearer $SWOOSHCONNECT_API_KEY" \
  --data-urlencode "since=$SINCE" \
  --data-urlencode "limit=200" \
  https://swooshrank.com/api/v1/connect/calls/log
ts
let since = new Date().toISOString();

async function handle(call: unknown): Promise<void> {
  /* your logic */
}

export async function poll(): Promise<void> {
  const url = new URL("https://swooshrank.com/api/v1/connect/calls/log");
  url.searchParams.set("since", since);
  url.searchParams.set("limit", "200");
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.SWOOSHCONNECT_API_KEY}` },
  });
  if (!res.ok) throw new Error(`calls_log failed: ${res.status}`);
  const { data } = await res.json();
  for (const call of data.calls) await handle(call);
  if (data.nextCursor) since = data.nextCursor;
}

You pay for what you connect. Nothing else.