LinkedIn safety and rate limits

The daily and weekly per-sender limits Swarmhit enforces, how to pace your API calls like a human, and exactly how to handle 429, 409 and 401 responses.

Everything in this guide exists to keep sender accounts safe. A restricted LinkedIn account is worth far more than any single day's sends, so Swarmhit enforces limits server-side and expects your integration to add human-like timing on top.

The one rule

Swarmhit applies LinkedIn's recommended daily and weekly limits automatically and caps the number of outstanding invitations. What your flow controls is timing: since your code decides when each call fires, space the calls like a human would. A tight loop, even while under the limits, is what gets accounts restricted. Add the delays and working hours below.

Daily limits (per sender, reset at UTC midnight)

Limits are counted per sender account, not per API key or workspace. The same counters apply whether an action runs through the REST API, the MCP server, or a campaign.

ActionEndpointMCP toolCap (default / max)Use per dayHard rule
Connection requestPOST /accounts/{id}/connectionssend_connection_request25 / 40≤ 20enforced: weekly cap (about 130/week free senders, 150/week paid) and ≤ 500 pending; stay near 100/week to be safe
Message (1st-degree)POST /accounts/{id}/messagessend_direct_message40 / 40≤ 40connections only
InMail (non-connection)POST /accounts/{id}/inmailssend_inmail2 / 40≤ 30open profiles free (about 800 / month); otherwise uses your plan's InMail credits
Profile viewPOST /accounts/{id}/profile-viewsvisit_profile40 / 40≤ 40real and visible to the target
Search importPOST /lead-imports(search import)1000 / 2500≤ 10002500 on Sales Navigator; free accounts are capped monthly

The search-import figures are the daily per-sender import volume. They are separate from the maxLeads parameter on a single import job (default 100, max 2500), covered in Leads, lists and imports.

Going over any cap returns 429 with details: { limit, used, scope, resetAt }, where scope is day, week, or pending. Stop that action for that sender until resetAt.

Reads are free: GET connection status, GET profile lookup, and DELETE (withdraw) are uncapped.

The network tolerates more on paid accounts (roughly 100 invites/day, 100 to 150 messages/day, 100 profile views/day), but the weekly invite cap is what you will actually hit: about 130/week for free senders, 150/week for paid, enforced server-side. Stay conservative (around 100 invites per week); do not chase the daily maximum.

Pace your calls (required)

  • 8 to 15 minutes between write actions per sender, randomized (never a fixed interval).
  • Working hours only, in the account's local time zone (about 9:00 to 18:00), weekdays.
  • Spread the day's volume across 2+ sessions; vary the daily total by ±20%.
  • Keep writes under about 5 to 10 per hour.

Campaigns handle this scheduling for you. These rules matter when you drive senders yourself through the direct actions endpoints.

Hygiene

  • Pre-check with the uncapped GETs (connection status, profile lookup) and skip anyone already pending or connected. Fewer wasted sends and 409s.
  • Withdraw stale pending invites (3 to 4 weeks old) via DELETE (uncapped); keep total pending under ~500. Too many open invites is itself a restriction trigger.
  • Re-inviting the same person too soon after a withdraw is rejected; they must wait about 3 weeks.
  • Personalize first touches. Invitation notes are a premium sender feature capped at 300 characters (longer notes are trimmed); invites from free senders are sent without the note (LinkedIn gives free accounts only a handful of note-invites per month). Never send identical templated messages at volume.

Error handling

SignalWhat to do
429 (scope: day / week / pending)Stop that action for that sender until resetAt; for pending, withdraw some invites first. No retry loop.
409 (already pending)Skip and move on.
401 / 403Stop; do not retry. A 401 is your API key; a rejected sender surfaces as 409 or an account webhook.
"rate-limiting, slow down"Back off hard and widen your gaps.

429: a sender limit was hit

A 429 means the sender hit its daily cap, its weekly connection-request limit (tier-based: about 130 invites per week for free sender accounts, 150 for paid), or the pending-invitation backlog.

{
  "error": {
    "status": 429,
    "message": "Daily limit reached for this action on this account (25/25).",
    "details": { "limit": 25, "used": 25, "scope": "day", "resetAt": "2026-06-19T00:00:00.000Z" }
  }
}

Branch on details.scope:

  • day or week: pause that action for that sender and schedule the next attempt at resetAt (UTC). Other actions and other senders are unaffected.
  • pending: there is no resetAt; the window clears as invitations are accepted or withdrawn. Withdraw stale invites, then resume.
  • inmail_credits (code: inmail_credits_exhausted): the sender is out of InMail credits. Stop sending InMails from it until the balance refills (visible as inmailCredits on the account).

Never sit in a retry loop on a 429. Retrying does not free the window and the burst pattern itself endangers the account.

409: a conflict

Branch on error.code where present:

  • Invitation already pending to this profile: skip the profile and move on. This is normal attrition, not a failure.
  • sender_paused: the sender is paused. Either resume it or route the send to another sender.
  • inmail_not_available: the sender has no premium InMail access. Do not retry; InMail needs a premium LinkedIn plan on the sender.
  • Sender rejected by LinkedIn: the account may need reconnecting. Stop sending from it and reconnect.
  • idempotency_in_progress: a request with the same Idempotency-Key is still in flight; retry shortly with the same key. idempotency_failed: the original errored after it may have taken effect; retry with a new key.

401 and 403: stop and alert

A 401 means the bearer key is invalid or revoked. A 403 means the workspace is not allowed to perform the action. Neither resolves on retry: stop the integration and alert a human.

A sender that loses its LinkedIn session is a separate case. Subscribe to the account.status_changed and account.checkpoint_required webhook events so you learn about it immediately, stop all sends from that sender, and reconnect it before resuming.

422 and 404: skip the profile

A 422 means LinkedIn could not complete the action on this target (cannot invite, already connected, or the recipient cannot be messaged by this sender). A 404 with code: profile_not_found means the profile URL matched nothing. Both are per-profile outcomes: log them, skip the profile, and never retry them in a loop.

"rate-limiting, slow down"

If an error message tells you to slow down, treat it as a serious signal even when you are under every documented cap. Back off hard and widen your gaps.

Example: a safe send loop

Use an Idempotency-Key on every send so a timeout retry can never double-send, and check connection status first.

# Free pre-check: skip anyone already pending or connected
curl "https://app.swarmhit.com/api/v1/accounts/ACCOUNT_ID/connections/john-doe" \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx"

# Send, spaced 8 to 15 minutes after the sender's previous write
curl -X POST "https://app.swarmhit.com/api/v1/accounts/ACCOUNT_ID/connections" \
  -H "Authorization: Bearer swh_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8b1f2c3d-a4e5-4f60-9b71-2c3d4e5f6a7b" \
  -d '{
    "profileUrl": "https://www.linkedin.com/in/john-doe",
    "note": "Hi John, enjoyed your post on outbound benchmarks. Would be glad to connect."
  }'

On 429, persist resetAt per sender and action, and gate future sends on it locally instead of rediscovering the limit with failed calls.

On this page