Errors And Limits
Every error type Direct answers, pacing and daily caps, retries, dry runs, Idempotency-Key and unknown outcomes.
Direct answers errors in Unipile's shape, with Unipile's status codes and error types, so code written for Unipile's errors keeps working.
{
"title": "Too many requests",
"detail": "LinkedIn asked this account to slow down.",
"type": "errors/too_many_requests",
"status": 429,
"retry_after": 900
}Branch on status and type. detail is for people: its words can change, and it never holds an account's data.
Every Error Type#
| HTTP | type |
Meaning |
|---|---|---|
400 |
errors/invalid_parameters |
A parameter or body field is invalid: a value of the wrong type (limit=abc), a limit out of range, a cursor this API didn't give, an id that isn't LinkedIn's, a text too long, a body that isn't JSON or a form, an Idempotency-Key used for another write. |
400 |
errors/invalid_url |
Hosted auth or webhooks: a URL that isn't https. |
400 |
errors/content_too_large |
A file over LinkedIn's limit (a message's file over 20 MB, an image over 36 MB, a video under 75 KB or over 5 GB). |
400 |
errors/missing_parameters |
A required parameter is missing: account_id on a read, api_url or reconnect_account on a hosted auth link. detail names it. |
401 |
errors/missing_credentials |
The X-API-KEY header is missing, wrong or revoked. |
401 |
errors/disconnected_account |
LinkedIn signed the account out, asks it for a security check, or its browser has no LinkedIn session. Reconnect it with a hosted auth link. |
402 |
errors/subscription_required |
Your workspace has no active trial or subscription, so its API keys answer this on every route. Start the free trial or add a card at lempile.com/dashboard/subscription and they work again right away. Your accounts stay connected and your webhooks keep coming meanwhile. |
403 |
errors/insufficient_permissions |
The account doesn't write through Direct (title Writes off), the post isn't the account's to delete, LinkedIn says the account may not do this, or a workspace key called one of Direct's internal admin routes. |
403 |
errors/resource_access_restricted |
LinkedIn refused this read or write for this account. |
403 |
errors/account_restricted |
LinkedIn says the account is restricted. |
403 |
errors/action_required |
LinkedIn asks the account to confirm its email address first. |
403 |
errors/feature_not_subscribed |
A Sales Navigator or Recruiter search, parameter or route: Direct serves LinkedIn's classic API. |
404 |
errors/resource_not_found |
An account Direct doesn't serve or that isn't your workspace's (title Account not found, the same answer for both), or nothing on LinkedIn with this id (a profile, a post, a chat, a message, an invitation, a webhook). |
409 |
errors/conflict |
A write with this Idempotency-Key is still running. |
415 |
errors/unsupported_media_type |
A voice note that isn't audio Direct can turn into LinkedIn's format. Nothing was sent. |
422 |
errors/invalid_parameters |
LinkedIn refused the write's content (title LinkedIn refused it). |
422 |
errors/already_connected |
Invitation: already a connection. |
422 |
errors/already_invited_recently |
Invitation: one is already waiting. |
422 |
errors/cannot_invite_attendee |
Invitation: the account itself, or someone whose invitation to the account is waiting. |
422 |
errors/invalid_recipient |
No profile with this id, or the account itself as a chat's recipient. |
422 |
errors/cannot_resend_yet |
LinkedIn's weekly invitation limit, or a withdrawn invitation sent again within about 3 weeks. The account's other calls go on. |
422 |
errors/limit_exceeded |
Too many invitations waiting for an answer: withdraw some. |
422 |
errors/connection_limit_reached |
The account reached LinkedIn's connection limit. |
422 |
errors/no_connection_with_recipient |
The account can't message this person for free: only an InMail would reach them. |
422 |
errors/not_allowed_inmail |
LinkedIn offers no InMail to this person from this account. |
422 |
errors/insufficient_credits |
No InMail credit for this person. |
422 |
errors/invalid_post |
LinkedIn refused the post. |
422 |
errors/unprocessable_entity |
LinkedIn refused another write (for example a post's deletion), or a message sent more than 60 minutes ago can't be deleted or edited. |
422 |
errors/not_supported_by_provider |
PATCH /chats/{chat_id} with one of Unipile's WhatsApp or Telegram actions. |
422 |
errors/invalid_message |
A voice note over 60 seconds. Nothing was sent. |
422 |
errors/action_already_performed |
The message is already deleted. |
429 |
errors/too_many_requests |
The account is resting, used up its day, or is busy. See Pacing. Always with retry_after. |
500 |
errors/provider_error |
LinkedIn answered something Direct couldn't read, or an error of its own. Nothing was written. |
500 |
errors/unexpected_error |
Direct failed. |
501 |
errors/feature_not_implemented |
Direct doesn't do this (yet): fall back to Unipile, or do without. |
503 |
errors/network_down |
The account's proxy or Lempile's browser server didn't answer. Nothing was sent. |
504 |
errors/request_timeout |
A read: LinkedIn didn't answer in time. A write: unknown outcome (see below). |
Parameter Errors#
A required query parameter that's missing, or one of the wrong type, answers Unipile's 400 too, naming the parameter (never the value you sent):
{ "title": "Missing parameters", "detail": "account_id is required.", "type": "errors/missing_parameters", "status": 400 }{ "title": "Invalid parameters", "detail": "limit: Input should be a valid integer, unable to parse string as an integer.", "type": "errors/invalid_parameters", "status": 400 }Cloudflare's Answers#
direct.lempile.com is behind Cloudflare. Two of its answers can reach you instead of Direct's:
524: the request took more than 100 seconds (a write waiting its turn, a large upload). For a write, it's an unknown outcome.413: the request body is over Cloudflare's limit (100 MB on most plans): a large video.
Pacing#
Every LinkedIn call of an account goes through that account's own pace:
| Limit | Value | When reached |
|---|---|---|
| One call at a time | A call waits its turn up to 60 seconds, then 429 with retry_after: 60. |
|
| Gap between calls | 1.5 to 3 seconds | Calls wait; nothing is refused. |
| LinkedIn calls in any 24 hours | 1,500 | 429 until the oldest of them is 24 hours old (retry_after says when). |
| Writes in any 24 hours | 150 | 429 until the oldest write is 24 hours old. |
Rest after LinkedIn's "too many" (429 or 999) |
15 minutes | Every call of the account answers 429 with the seconds left. |
- The limits are per account, never per key: two apps using one account share its budget.
- One request can cost several LinkedIn calls: a profile with sections, a long page, a write's checks and uploads. Each route's page says what it reads.
- The webhook reads use the same budget (about 300 calls a quiet day, 450 at most; see Webhooks).
- A security check (
errors/disconnected_account) is never retried. - The counters live in Direct's memory and start again when Direct restarts.
Retrying#
| Answer | Retry? |
|---|---|
429 |
Yes, after retry_after seconds (also in the Retry-After header). |
503 |
Yes: nothing was sent. |
500 provider_error |
A read: yes, later. A write: nothing was written; retry once. |
504 on a read |
Yes. |
504 on a write, 524, a lost connection |
Only with the same Idempotency-Key (below), or after checking the write didn't happen. |
401 disconnected_account |
No: reconnect the account. |
Other 4xx, 501 |
No. |
Writes#
Dry Runs#
Every write takes ?dry_run=true. Direct does everything up to the write itself (it reads what it checks: the profile before an invitation, the "Message" option before a new chat, the post before its deletion) and answers the plan instead of sending it:
{
"object": "DryRun",
"route": "posts.react",
"method": "POST",
"path": "/voyager/api/voyagerSocialDashReactions?threadUrn=urn%3Ali%3Aactivity%3A7388000000000000000",
"body": { "reactionType": "LIKE" },
"status": 201
}- Status
200.statusis what the real write would answer.uploads,multiphotoandcheckedappear when the write has files or checks. - Nothing is sent to a LinkedIn write endpoint, and no file is uploaded. The checks' reads do happen and count in the account's calls.
- Dry runs work for every served account, writes on or off, and don't use an
Idempotency-Key. - A refusal found by the checks (
already_connected,no_connection_with_recipient…) answers as the real write would.
Idempotency-Key#
Send an Idempotency-Key header (1 to 200 characters, unique per write) on every real write:
- The key is claimed (per account) before anything is sent. The same key on the same account never writes twice.
- A repeat after the write finished answers the first answer again (same status, same body; an empty
204stays empty), for 7 days. - A repeat while the first is still running answers
409 errors/conflict. - A write that surely didn't happen (a refusal,
429,503,500 provider_error) frees the key: the same key may try again. A refusal found by the checks before sending never takes the key at all. - A write whose outcome is unknown answers the same
504to every repeat: it never writes again under that key. - The same key on another route answers
400.
With a key, retrying after a timeout, a 524 or a lost connection is safe: you get the first answer, 409 while it runs, or the write if it never started.
Unknown Outcome#
A write is tried again by Direct only when nothing reached LinkedIn (the proxy refused the connection, or LinkedIn refused the session before reading it). When LinkedIn may have received it (a timeout after sending, or an answer that doesn't name what it made), Direct answers:
{
"title": "Unknown outcome",
"detail": "LinkedIn may have received this write; it isn't tried again. Check before sending it once more.",
"type": "errors/request_timeout",
"status": 504
}Check before writing again: the chat's latest messages, the account's posts, the post's comments, the sent invitations.
Other Limits#
| Page sizes | Each list's limit range is on its page (relations up to 1,000; chats, messages and attendees up to 250; most others up to 100). |
| Cursors | Opaque; only the list that gave one takes it back. |
| Texts | Message 8,000 characters, post 3,000, comment 1,250, invitation note 300, InMail subject 200. |
| Files | Message: 20 MB each. Post: up to 20 images of 36 MB, or one video of 75 KB to 5 GB. Comment: one image. |
| Webhooks | 20 headers each; one request_url and one source each. |
Was this page helpful?