Limits, errors and polling
Exact rate limits, body caps, import caps, the error envelope, pagination, and how to poll because there are no webhooks.
Could not copy. Use View as Markdown instead.
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-RemainingandX-RateLimit-Reset. Reset is a timestamp in milliseconds since the epoch. - A blocked request answers HTTP 429. It usually carries
Retry-Afterin whole seconds. Do not rely on that header being present.
{ "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
| Door | Cap | Over the cap |
|---|---|---|
| REST | 2,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
| Tool | Cap |
|---|---|
dialer_import_contacts, csv | 20,000 contacts per call. The csv text is at most 2,000,000 characters. |
dialer_import_contacts, rows | 5,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.
{ "error": { "code": "not_found", "message": "No campaign with that id." } }| Code | HTTP | Meaning |
|---|---|---|
| unauthorized | 401 | The API key is missing, invalid, or revoked. |
| invalid_input | 400 | The request body or parameters failed validation. The issues field lists each problem. |
| not_found | 404 | The account, review, post, campaign, contact or agent referenced doesn't exist for your organization. |
| not_supported | 400 | The action isn't available on this platform. |
| payment_required | 402 | A payment method is needed, or paid accounts are paused until a renewal payment clears. The reason field says which. |
| provider_auth | 401 | The platform no longer accepts the stored authorization. Reconnect the account. |
| provider_permission | 403 | The connected account lacks a permission this action needs on the platform. |
| rate_limited | 429 | Too many requests; retry after the interval in the response. |
| provider_error | 502 | The platform returned an error. |
| transient | 503 | A temporary failure on the platform's side. Retry. |
| payload_too_large | 413 | The 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
sinceto the current time as an ISO 8601 date-time. Store it. - Call
calls_logwithsinceset to your stored value. Uselimitup to 200. The default is 50. - Handle every call in
calls. - If
nextCursoris a string, store it as the newsince. 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.
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/loglet 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.