> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gtm-api.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LinkedIn Recruiter

> Recruiter is supported on accounts that hold a Recruiter seat: candidate search, the talent inbox, hiring projects and Recruiter InMail. What the seat gives you, and why the session expires every 30 days.

Recruiter is supported. An account that holds a LinkedIn Recruiter seat can run candidate searches, sync the talent inbox, read its hiring projects and send Recruiter InMail.

Everything on this page needs that seat. Without one, every Recruiter call answers `422 recruiter_required`.

## What the seat gives you

| Surface | Where it is documented |
| - | - |
| Candidate search, with the full Recruiter facet panel | [People and company search](/kb/people-and-company-search) |
| The talent inbox, on its own sync cadence | [Inbox and message sync](/kb/inbox-and-message-sync) |
| Hiring projects | API reference |
| Recruiter InMail | API reference |

Recruiter is a **third surface**, separate from the basic LinkedIn engine and from Sales Navigator. Its threads carry `messenger_type: "recruiter"`, its candidates are addressed by `talent_id`, and its search facet ids come from a lookup of their own: none of the three id spaces are interchangeable.

## How the seat is detected

The premium check stamps the seat onto the account (`recruiter_seat_id`). Run it with `checks: ['recruiter']` to refresh just that one.

Some calls need the stamped seat number, not only the seat itself. If it is missing they answer `422 recruiter_seat_unresolvable`, and the premium check resolves it.

## Several Recruiter contracts

A member who holds seats on several Recruiter contracts is asked by LinkedIn to pick one before Recruiter opens, and holds no seat until they do. The premium check handles the common case on its own: when only one corporate contract is on offer (or the account is already bound to one of them) it binds the browser to that contract and stamps the seat. When several corporate contracts are on offer the check does not guess, because the wrong pick binds the browser to the wrong inbox: the seat stays unstamped until someone picks. An agent lists the contracts with `get_linkedin_account_my_recruiter_contracts` (the corporate rows are Recruiter seats; the individual row is a Job Posting contract on the personal account) and binds one with `select_linkedin_account_recruiter_contract`, passing `contract_id` exactly as listed; the seat holder can pick it in the account's browser instead. Either way the seat is stamped on the spot or by the next check. `recruiter_contract_id` on the account names the contract the seat belongs to.

## The Recruiter session expires every 30 days

**This is the failure everyone hits, and it is not a fault.**

Recruiter keeps its own session, roughly 30 days long, separate from the account's normal LinkedIn session. The account can be perfectly healthy and still have a dead Recruiter session. When it runs out, LinkedIn asks the seat holder for their password again before Recruiter opens.

The account's `recruiter_status` says where it stands: `active` (the seat answers), `signed_out` (the seat needs a sign-in) or `null` (no Recruiter seat). It flips to `signed_out` on the first refused Recruiter call, and the platform also checks the session clock every 15 minutes so you hear about it before a sync runs into it. The moment it flips you get one webhook, `linkedin-accounts.recruiter-signed-out`, and the person who connected the account gets one email; nothing repeats until the next time, and never more than once a day per account.

The webhook's `reason` tells the two cases apart. `session_expired` and `wire_401` mean LinkedIn ended a session that existed. `session_unconfirmed` means the seat is there but the browser has never been signed in to Recruiter (or the member is still on LinkedIn's contract chooser): nothing was ended, and the email says "sign in to switch it on" rather than "you were signed out". A freshly connected account with a Recruiter seat starts in exactly this state, so the first thing to do after connecting a seat holder is the sign-in below.

While the seat is signed out, every Recruiter call answers `409 recruiter_reauth_required` and the recruiter inbox sync waits. The error carries `context.account_url`, the account's page in the app, and a plain suggestion an agent can relay.

**Only the seat holder can sign in again.** Open the account's page, press **Sign in to Recruiter**: the account's cloud browser opens straight on Recruiter's sign-in, you enter the LinkedIn password there and confirm. The platform restarts the browser, re-checks the seat and flips the status back to `active` (and sends `linkedin-accounts.recruiter-signed-in`). Nobody else can do it for you: not another teammate, not support, not an agent, and the platform never stores the password. Retrying the call does not help until you have signed in.

`recruiter_session_expires_at` on the account tells you when the session will run out, so you can sign in before it does rather than after. The platform reads it from the account's own running browser. It is null when the platform has not seen the session cookie, which is not a verdict: a browser that is still on LinkedIn's contract chooser has no session cookie at all, and the fix there is picking a contract, which the sign-in above ends on.

## InMail credits

Recruiter InMails come out of the contract's own pool, which LinkedIn keeps apart from the Sales Navigator seat's grant and from the Premium plan's balance. The account carries it as `recruiter_inmail_credits`, what Recruiter's Usage Overview shows as InMail credits. The premium check reads it for an `active` seat as part of its `inmail_credits` step, every 6 hours in the background or on demand with `check_linkedin_account_premium_subscription` and `checks: ["inmail_credits"]`. Null means the platform has not read it yet, never zero.

A new thread on a balance known to be zero is refused before anything reaches LinkedIn (rule `no_inmail_credits`); a reply into an existing thread is never guarded. A signed-out seat keeps its last number until the seat holder signs in again. `search_linkedin_accounts` filters on the field, so `recruiter_inmail_credits: {eq: 0}` lists the seats that cannot open a thread until the monthly refresh.

## Limits that come from LinkedIn, not from us

**One InMail per candidate per 24 hours.** Sending a second one answers `429 recruiter_inmail_cooldown` with `cooldown_ends_at`. If a reply may have landed in between, refresh the thread before assuming the send failed.

**A per-seat search throttle.** Heavy searching answers `429 recruiter_search_usage_limit`. Waiting a minute clears it.

Neither is a platform limit and neither is configurable here. They are LinkedIn's, applied to the seat.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.