Profile & Company

The profile and company commands let you read LinkedIn profile data, manage your own profile, follow/unfollow members, list connections and endorsements, and fetch company information — plus your connected account's own insight surface (subscription, analytics, Social Selling Index, profile visitors) and, for company pages you administer, follower lists and the admin message inbox (Beta). All profile subcommands are account-scoped and require --account (or a default account set via curviate config set-account).

The <id> argument on most commands accepts a LinkedIn profile URL, a public slug, or a URN — the CLI normalizes the input automatically.

Changed in 0.15.0: profile connections is renamed profile relations. The --notify flag on profile <id> / profile get is removed — signal-a-view has no v2 request-side field. profile endorse's --skill flag is renamed --endorsement-id (same value — the endorsement id from the target's skills section — semantics unchanged). New: profile update, profile follow, profile unfollow, profile following (alongside the existing profile followers, now also a standalone command).

Commands

# Get your own profile (the authenticated LinkedIn user)
curviate profile me
# Get any member's profile
# <id> accepts a LinkedIn URL, slug, or URN
curviate profile <id>
 
# Flags for supplementary data (can be combined):
#   --posts              Include recent posts
#   --is-company         When --posts is set, treat the account as a company page
#   --comments           Include recent comments
#   --reactions          Include recent reactions
#   --followers          Include follower count
#   --sections           Comma-separated LinkedIn sections to fetch (e.g. experience,education)

profile me and profile <id> are backed by the identical LinkedIn profile response and share the same slim --json shape, with two differences: profile me additionally returns emails and is_premium (not visible for other members), and profile <id> additionally returns network_distance (not meaningful for your own profile). headline reads LinkedIn's own profile headline; the longer About-section paragraph is not part of the slim projection — pass --sections about with --verbose for that. current_position is null unless --sections experience was requested; when present it's synthesized from the first experience entry as { title, company_name, company_id: null, is_current }.

profile <id>:

{
  "provider_id": "ACoAACyJnqkBaYexZ5xJAQK5o78ddmR0UC3N5DI",
  "first_name": "Raphael",
  "last_name": "Redmer",
  "headline": "Founder @RedHire 🔥",
  "location": "Germany",
  "network_distance": "OUT_OF_NETWORK",
  "public_identifier": "raphael-redmer",
  "current_position": null
}

profile me:

{
  "provider_id": "ACoAACyJnqkBaYexZ5xJAQK5o78ddmR0UC3N5DI",
  "first_name": "Raphael",
  "last_name": "Redmer",
  "headline": "Founder @RedHire 🔥",
  "public_identifier": "raphael-redmer",
  "location": "Germany",
  "emails": ["raphael@example.com"],
  "is_premium": true,
  "current_position": null
}
# List your 1st-degree connections (paginated)
curviate profile relations [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# List a member's followers (paginated read, accepts 'me')
curviate profile followers <id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# List who a member follows (paginated read, accepts 'me')
curviate profile following <id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# Follow a member (write, bodyless, --preview accepted)
# Sends a connect request instead if the member's profile is private
curviate profile follow <id>
# Unfollow a member (write, bodyless, idempotent, --preview accepted)
curviate profile unfollow <id>
# Update your own profile (write, --preview accepted)
# --skills is add-only (no removal via this command); there is no --description flag
curviate profile update \
  [--headline <text>] \
  [--bio <text>] \
  [--first-name <name>] \
  [--last-name <name>] \
  [--skills <name1,name2>] \
  [--picture <image-file>] \
  [--background-picture <image-file>]
# Endorse a skill on a member's profile (write, --preview accepted)
# --endorsement-id takes the endorsement id from the target's skills section (not a plain skill name)
curviate profile endorse <id> --endorsement-id <endorsement_id>

Your own insights

These are self-reads of the connected account's own analytics surface — they take no target and require only --account. All are reads (no --preview); subscription, analytics, and ssi are scalar, while visitors is paginated.

# Your own premium subscription — entitlements, plan title, management links
# A free account is a valid result (has_premium:false, plan_title:null), not an error
curviate profile subscription
# Your own performance headline metrics (scalar read)
# Profile viewers (90d), post impressions (7d), search appearances (last completed
#   week), followers (running total) — fixed windows, no window selector.
# A count of 0 is a real zero; a per-metric null means that card was unavailable.
curviate profile analytics
# Your own Social Selling Index (scalar read)
# Overall score, the four pillar breakdowns, and industry/network percentile ranks.
# A zero-activity account returns all scalars null with active_seat false.
curviate profile ssi
# People who recently viewed your profile (paginated read)
# Each viewer is classified by disclosure fidelity: identified, semi-anonymous,
#   or aggregate. A Premium account sees identified viewers; a free account is
#   capped at a lower fidelity (still a success, never a permission error).
curviate profile visitors [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]

For profile visitors, walk the cursor: a non-null cursor means more may exist even if a page held zero identified individuals — page until cursor is null.

# Fetch a company's profile (read-only)
# <id> accepts a LinkedIn URL, slug, or URN
# --preview and --all are not accepted on this command
# --account is not required
curviate company <id>

company <id> returns:

{
  "id": "2135371",
  "name": "Stripe",
  "public_identifier": "stripe",
  "profile_url": "https://www.linkedin.com/company/stripe/",
  "industry": ["Computer Software", "Technology, Information and Internet"],
  "employee_count": 17356,
  "employee_count_range": { "from": 5001 },
  "website": "https://stripe.com",
  "establishment_year": 2010,
  "headquarters": {
    "city": "South San Francisco",
    "country_code": "US",
    "postal_code": "94080",
    "area": "California"
  },
  "follower_count": 1550419
}

employee_count_range.from is the range's lower bound only — LinkedIn's headcount ranges are open-ended at the top, so there is no to. headquarters (and each of its four fields) is null when not on file for the company. There is no messaging field on this resource.

# List people who currently work at the company (paginated read)
# <id> is the company's numeric provider_id (the id field of company <id>)
curviate company employees <id> [--keywords <k>] [--location <location_parameter_id>] [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# List the company's posts (paginated read)
curviate company posts <id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# List the company's open job postings (paginated read)
curviate company jobs <id> [--keywords <k>] [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]

Company pages you administer

These commands act on LinkedIn pages your connected account administers. The <id> accepts a URL, slug, or numeric id — a URL/slug is resolved to the numeric provider_id first.

# List the pages your connected account administers (paginated read)
# The id on each page is the numeric provider_id the followers /
#   invitable-followers / employees / posts / jobs sub-resources consume.
# An empty list is valid (you administer no pages).
curviate company managed [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]

company managed returns a slim default — id, name, follower_count, can_invite_to_follow, and the rest of the small scalar fields — and, with --verbose, also capabilities[] and permissions{}.

# List a company page's followers, newest first (paginated read)
# Your connected account must administer the page (see `company managed`).
curviate company followers <id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# List your connections invitable to follow the page (paginated read)
# Your account must administer the page. An empty list is valid (nobody currently invitable).
curviate company invitable-followers <id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]

Each item is { object, id, profile_urn, invite_token }; there is no name or headline on this resource, so hydrate a candidate via profile <id> before deciding who to invite. invite_token is always base64-encoded in CLI output (the raw value can carry bytes that are unsafe to print in a terminal); decode with Buffer.from(token, "base64").toString("utf8") if you need the original. Pass the id field (not invite_token) to company follow-invite.

# Invite connections to follow the page (write, admin-gated, --preview accepted)
# --invitee takes the id field from `company invitable-followers` (repeatable, 1-50 per request)
# Preview renders the resolved request (including the resolved numeric company id) without sending
curviate company follow-invite <id> --invitee <AC_member_id> [--invitee <AC_member_id> ...] --preview
 
curviate company follow-invite <id> --invitee <AC_member_id> [--invitee <AC_member_id> ...]

company follow-invite returns one outcome per requested invitee, in request order, for an all-valid request. If any invitee id is invalid, the whole request is rejected (404), with no partial success.

{
  "object": "company_follow_invite_result",
  "results": [
    {
      "object": "company_follow_invite",
      "invitee_id": "ACoAACyJnqkBaYexZ5xJAQK5o78ddmR0UC3N5DI",
      "status": "invited",
      "invitation_id": "urn:li:fsd_invitation:7042371002391",
      "error": null
    },
    {
      "object": "company_follow_invite",
      "invitee_id": "ACoAABzKpaLoBfEy26cX9dOJdEpO32rZ4kmZ8Ac",
      "status": "already_invited",
      "invitation_id": "urn:li:fsd_invitation:7042370987654",
      "error": null
    }
  ]
}

status is one of invited (a new invitation was created), already_invited (a pending invitation already existed; re-inviting is a safe no-op, the same invitation_id, never a duplicate), ineligible (not an invitable 1st-degree connection), or not_found (the member id did not resolve). invitation_id is present for invited/already_invited and null for a per-invitee failure, in which case error carries a canonical { code, message }.

Company inbox (Beta)

Beta. The company-page admin inbox commands are verified for single-page listing; deep pagination against a busy inbox is still being validated. Your connected account must administer the page; message content passes through verbatim and is never stored.

# List the conversations in a company page's admin inbox (paginated read)
curviate company chats <id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# Retrieve one conversation from the admin inbox (scalar read)
curviate company chat <id> <chat_id>
# List a conversation's messages, newest first (paginated read)
# Each item carries sender: {id, name}. There is no is_sender flag —
#   determine direction by comparing sender.id to your own member id.
curviate company messages <id> <chat_id> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]
# Retrieve one message from a conversation (scalar read)
curviate company message <id> <chat_id> <message_id>
# Search or filter the admin inbox (paginated read)
# Pass EXACTLY ONE mode — the three are mutually exclusive (exit 2 otherwise):
#   --query <text>   matches participant names and message content
#   --topic <card>   one inbox topic (1-5 or its name)
#   --unread         unread conversations only
curviate company search-chats <id> (--query <text> | --topic <card> | --unread) [--limit <n>] [--cursor <token>] [--all]

Reply to a company inbox

company reply sends into an existing company-inbox conversation, as the page, admin-gated (write, --preview accepted). It is reply-only, there is no way to start a fresh conversation on the page's behalf through this command.

<chat_id> is the 2-… conversation id from company chats / company messages above, passed through verbatim. The endpoint is company-scoped, so it resolves the page mailbox from <id> internally, there is no separate send-ready id to fetch. A malformed id returns a plain 400; a page the account does not administer returns 403.

# Preview the request first, then send it
curviate company reply <id> <chat_id> "Thanks for reaching out, happy to help." --preview
 
curviate company reply <id> <chat_id> "Thanks for reaching out, happy to help." [--attach <file>]

The send response echoes sent_as, and by default company reply prints a plain confirmation to stderr right after the send, Sent as RedHire (company page). company reply --preview prints Will send as a company page for the same reason, derived from the <id> of the page you are replying as, a zero-network-call operation.

Examples

Get your own profile

curviate profile me --account acc_YOUR_ACCOUNT_ID

Get a member's profile with posts

# Using a LinkedIn URL
curviate profile https://linkedin.com/in/janesmith --posts --account acc_YOUR_ACCOUNT_ID
 
# Using a URN
curviate profile urn:li:member:123456789 --posts --account acc_YOUR_ACCOUNT_ID
 
# Using a slug
curviate profile janesmith --posts --account acc_YOUR_ACCOUNT_ID

List all connections (streaming)

curviate profile relations --all --account acc_YOUR_ACCOUNT_ID

Follow and unfollow a member

curviate profile follow janesmith --account acc_YOUR_ACCOUNT_ID
 
curviate profile unfollow janesmith --account acc_YOUR_ACCOUNT_ID

List your own followers and who you follow

curviate profile followers me --account acc_YOUR_ACCOUNT_ID
 
curviate profile following me --all --account acc_YOUR_ACCOUNT_ID

Update your headline and photo

curviate profile update \
  --headline "Building agent-native LinkedIn infrastructure" \
  --picture ./new-avatar.jpg \
  --account acc_YOUR_ACCOUNT_ID

Preview an endorsement without sending it

curviate profile endorse janesmith --endorsement-id urn:li:skill:12345 --preview --account acc_YOUR_ACCOUNT_ID

Fetch a company profile

curviate company https://linkedin.com/company/acmecorp --account acc_YOUR_ACCOUNT_ID

Browse a company's people and jobs

# <id> is the numeric provider_id returned by `company <id>`
curviate company employees 1234567 --keywords "machine learning" --account acc_YOUR_ACCOUNT_ID
 
curviate company jobs 1234567 --account acc_YOUR_ACCOUNT_ID

Read your own insights

curviate profile analytics --account acc_YOUR_ACCOUNT_ID
 
curviate profile ssi --account acc_YOUR_ACCOUNT_ID
 
curviate profile visitors --limit 20 --account acc_YOUR_ACCOUNT_ID

Manage a company page you administer

# Find a page you administer, then list its followers
curviate company managed --account acc_YOUR_ACCOUNT_ID
 
curviate company followers 1234567 --all --account acc_YOUR_ACCOUNT_ID

Invite connections to follow a page you administer

# List who is invitable, preview the request, then send it
curviate company invitable-followers 1234567 --account acc_YOUR_ACCOUNT_ID
 
curviate company follow-invite 1234567 --invitee ACoAACyJnqkBaYexZ5xJAQK5o78ddmR0UC3N5DI --preview --account acc_YOUR_ACCOUNT_ID
 
curviate company follow-invite 1234567 \
  --invitee ACoAACyJnqkBaYexZ5xJAQK5o78ddmR0UC3N5DI \
  --invitee ACoAABzKpaLoBfEy26cX9dOJdEpO32rZ4kmZ8Ac \
  --account acc_YOUR_ACCOUNT_ID

Browse a company page's admin inbox (Beta)

# List conversations, then read one and search for unread
curviate company chats 1234567 --account acc_YOUR_ACCOUNT_ID
 
curviate company messages 1234567 2-abc123 --account acc_YOUR_ACCOUNT_ID
 
curviate company search-chats 1234567 --unread --account acc_YOUR_ACCOUNT_ID

Reply to a company inbox conversation, as the page

# List the page's inbox to get the 2-… chat id, then preview and send the reply
curviate company chats 1234567 --account acc_YOUR_ACCOUNT_ID
 
curviate company reply 1234567 2-YTQ3ODU3Njgt "Thanks for reaching out, happy to help." --preview --account acc_YOUR_ACCOUNT_ID
 
curviate company reply 1234567 2-YTQ3ODU3Njgt "Thanks for reaching out, happy to help." --account acc_YOUR_ACCOUNT_ID
COMPANY · LEGAL

Privacy Policy

Redmer Holding GmbHLast updated July 25, 2026

Who we are

Curviate is operated by Redmer Holding GmbH ("Curviate", "we", "us"), a German GmbH registered at Amtsgericht Bonn, HRB 29957, registered address Hostertstraße 16, 53332 Bornheim, Germany. Full company details are on our Imprint. We haven't appointed a statutory Data Protection Officer, since our processing doesn't reach the scale or sensitivity that requires one. Privacy questions go to privacy@curviate.com.

The two roles we play

When you create an account and use Curviate, we process your own data (identity, billing, API keys, connector authorizations). For that data, we are the controller.

When you use Curviate to act on your own connected LinkedIn account, viewing profiles, sending messages, managing engagement, that content and those contacts belong to that account and its people. You are the controller of that data; we are the processor, acting only on your instructions, under the Data Processing Agreement between us. If one of your contacts has a question about being reached through Curviate, you're who they should contact first; email privacy@curviate.com if you need help routing it.

What we collect, and why

DataWhy
Account identity (name, email, sign-in method)Create and secure your account
Your LinkedIn credentialsOperate the actions you request
LinkedIn content returned by an API callFulfil that specific request, nothing more
API keys and connector (OAuth) authorizationsAuthenticate your API, CLI, MCP, or SDK requests
Billing detailsCharge you correctly and meet our tax obligations
Usage and security logsKeep the service reliable and abuse-free
Support messagesRespond to you
Website analytics, only if you opt inUnderstand how the site is used

We rely on our contract with you, our legitimate interest in running and securing the service, our legal obligations (tax law, for example), and, for analytics, your consent. We never sell your data or use it to train models.

Where it's processed, and who else touches it

Our infrastructure runs in the EU. Hosting: Railway. Database and auth: Supabase, Ireland. Email: Resend. Payments: Stripe. Network security: a DDoS-protection provider sits in front of our app and never sees or stores request content. LinkedIn connectivity: a third-party infrastructure provider that lets us execute LinkedIn actions on your behalf. Error tracking: Sentry, Frankfurt. Product analytics: PostHog, Frankfurt. Uptime monitoring: Better Stack.

We give the current, named list of every provider above, plus our Data Processing Agreement, to any customer who asks: security@curviate.com.

Outside the EU

All customer LinkedIn data, account data, and telemetry are processed and stored exclusively in EU regions of our sub-processors. A few providers we rely on (Stripe and Sentry, for example) are headquartered outside the EU/EEA; where that applies, it's covered by their own GDPR safeguards, typically the EU Standard Contractual Clauses.

How long we keep it

DataRetention
Account and workspace dataWhile your account is active
Closed accountRecoverable for 7 days, then deleted on day 8
LinkedIn credentialsUntil you disconnect that account
LinkedIn contentNot stored; any transient cache clears within 1 hour, never indexed, never used for training
API keysUntil you revoke or rotate them
Connector (OAuth) authorizationsAccess token ~1 hour; refresh token up to ~12 months, or until you revoke it, whichever comes first
Billing recordsAs required by German tax law, currently up to 10 years
LogsA short operational window; metadata only, never message content

The 12-month figure above is a server-side credential for a connected AI agent or app. It is not a cookie and doesn't touch your browser session; see Cookies below for that. You can see and revoke every connector from Authorized applications in your dashboard at any time.

Cookies

We keep cookies to a minimum, and ask before anything beyond the essentials runs.

Strictly necessary, no consent needed:

NamePurposeExpiry
cc_cookieRemembers your cookie choice12 months
curviate-themeRemembers light/dark mode (local storage, not a cookie)Persistent
sb-*-auth-tokenKeeps you signed inWhile active; cleared on sign-out

Analytics, only if you accept:

NamePurposeExpiry
_gaGoogle Analytics: distinguishes visitors2 years
_gidGoogle Analytics: distinguishes visitors24 hours
_ga_<container id>Google Analytics: persists session state2 years

No advertising cookies, ever. Accept and reject are equally easy, and you can change your mind any time via Cookie Preferences in the footer; we won't ask again for 12 months unless something material changes. Our LinkedIn connect flow and OAuth authorization screen never set anything beyond the essentials, so no banner appears there.

Connecting an AI agent or app

Curviate is built for AI agents and automated clients as much as for people. If you connect an app like Claude, or your own code, via an API key or an OAuth connector, it can act on your workspace within the access you gave it. What it does with anything it receives back, including what it sends to its own AI model, is between you and that provider; review its practices before connecting it. Review and revoke any connection any time from your dashboard.

Your rights

You can access, correct, delete, restrict, or object to your data, port it elsewhere, and withdraw consent at any time: email privacy@curviate.com. Closing your account starts the 7-day recoverable window above. We don't make automated decisions about you that have a legal or similarly significant effect. You can also complain to a supervisory authority; ours is the Landesbeauftragte für Datenschutz und Informationsfreiheit Nordrhein-Westfalen (LDI NRW), www.ldi.nrw.de, though you're free to complain to the one in your own country instead.

Keeping it secure

Credentials are encrypted and never logged, returned, or shared. LinkedIn actions run through native, humanized flows; full detail is on our Security & Compliance page. If a breach puts your rights at risk, we'll notify the authorities and you, as GDPR requires. Curviate isn't directed at, or offered to, anyone under 16.

Changes

We'll update this page when our practices change, and reset the cookie prompt if the change is material.

Contact