universal-mcp-applications 0.1.33__py3-none-any.whl → 0.1.39rc16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp/applications/BEST_PRACTICES.md +1 -1
- universal_mcp/applications/ahrefs/app.py +92 -238
- universal_mcp/applications/airtable/app.py +36 -135
- universal_mcp/applications/apollo/app.py +124 -477
- universal_mcp/applications/asana/app.py +605 -1755
- universal_mcp/applications/aws_s3/app.py +63 -119
- universal_mcp/applications/bill/app.py +644 -2055
- universal_mcp/applications/box/app.py +1246 -4159
- universal_mcp/applications/braze/app.py +410 -1476
- universal_mcp/applications/browser_use/README.md +15 -1
- universal_mcp/applications/browser_use/__init__.py +1 -0
- universal_mcp/applications/browser_use/app.py +91 -26
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +103 -242
- universal_mcp/applications/canva/app.py +75 -140
- universal_mcp/applications/clickup/app.py +331 -798
- universal_mcp/applications/coda/app.py +240 -520
- universal_mcp/applications/confluence/app.py +497 -1285
- universal_mcp/applications/contentful/app.py +40 -155
- universal_mcp/applications/crustdata/app.py +44 -123
- universal_mcp/applications/dialpad/app.py +451 -924
- universal_mcp/applications/digitalocean/app.py +2071 -6082
- universal_mcp/applications/domain_checker/app.py +3 -54
- universal_mcp/applications/e2b/app.py +17 -68
- universal_mcp/applications/elevenlabs/README.md +27 -3
- universal_mcp/applications/elevenlabs/app.py +741 -74
- universal_mcp/applications/exa/README.md +8 -4
- universal_mcp/applications/exa/app.py +415 -186
- universal_mcp/applications/falai/README.md +5 -7
- universal_mcp/applications/falai/app.py +156 -232
- universal_mcp/applications/figma/app.py +91 -175
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +198 -176
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +92 -529
- universal_mcp/applications/fpl/utils/fixtures.py +15 -49
- universal_mcp/applications/fpl/utils/helper.py +25 -89
- universal_mcp/applications/fpl/utils/league_utils.py +20 -64
- universal_mcp/applications/ghost_content/app.py +70 -179
- universal_mcp/applications/github/app.py +30 -67
- universal_mcp/applications/gong/app.py +142 -302
- universal_mcp/applications/google_calendar/app.py +26 -78
- universal_mcp/applications/google_docs/README.md +15 -14
- universal_mcp/applications/google_docs/app.py +103 -206
- universal_mcp/applications/google_drive/app.py +194 -793
- universal_mcp/applications/google_gemini/app.py +68 -59
- universal_mcp/applications/google_mail/README.md +1 -0
- universal_mcp/applications/google_mail/app.py +93 -214
- universal_mcp/applications/google_searchconsole/app.py +25 -58
- universal_mcp/applications/google_sheet/README.md +2 -1
- universal_mcp/applications/google_sheet/app.py +226 -624
- universal_mcp/applications/google_sheet/helper.py +26 -53
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/README.md +10 -32
- universal_mcp/applications/heygen/app.py +339 -811
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/README.md +1 -1
- universal_mcp/applications/hubspot/app.py +7508 -99
- universal_mcp/applications/jira/app.py +2419 -8334
- universal_mcp/applications/klaviyo/app.py +739 -1621
- universal_mcp/applications/linkedin/README.md +18 -1
- universal_mcp/applications/linkedin/app.py +729 -251
- universal_mcp/applications/mailchimp/app.py +696 -1851
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +333 -815
- universal_mcp/applications/ms_teams/app.py +420 -1407
- universal_mcp/applications/neon/app.py +144 -250
- universal_mcp/applications/notion/app.py +38 -53
- universal_mcp/applications/onedrive/app.py +26 -48
- universal_mcp/applications/openai/app.py +43 -166
- universal_mcp/applications/outlook/README.md +22 -9
- universal_mcp/applications/outlook/app.py +403 -141
- universal_mcp/applications/perplexity/README.md +2 -1
- universal_mcp/applications/perplexity/app.py +161 -20
- universal_mcp/applications/pipedrive/app.py +1021 -3331
- universal_mcp/applications/posthog/app.py +272 -541
- universal_mcp/applications/reddit/app.py +65 -164
- universal_mcp/applications/resend/app.py +72 -139
- universal_mcp/applications/retell/app.py +23 -50
- universal_mcp/applications/rocketlane/app.py +252 -965
- universal_mcp/applications/scraper/app.py +114 -142
- universal_mcp/applications/semanticscholar/app.py +36 -78
- universal_mcp/applications/semrush/app.py +44 -78
- universal_mcp/applications/sendgrid/app.py +826 -1576
- universal_mcp/applications/sentry/app.py +444 -1079
- universal_mcp/applications/serpapi/app.py +44 -146
- universal_mcp/applications/sharepoint/app.py +27 -49
- universal_mcp/applications/shopify/app.py +1748 -4486
- universal_mcp/applications/shortcut/app.py +275 -536
- universal_mcp/applications/slack/app.py +43 -125
- universal_mcp/applications/spotify/app.py +206 -405
- universal_mcp/applications/supabase/app.py +174 -283
- universal_mcp/applications/tavily/app.py +2 -2
- universal_mcp/applications/trello/app.py +853 -2816
- universal_mcp/applications/twilio/app.py +27 -62
- universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
- universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
- universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
- universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
- universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
- universal_mcp/applications/whatsapp/app.py +35 -186
- universal_mcp/applications/whatsapp/audio.py +2 -6
- universal_mcp/applications/whatsapp/whatsapp.py +17 -51
- universal_mcp/applications/whatsapp_business/app.py +86 -299
- universal_mcp/applications/wrike/app.py +80 -153
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +120 -306
- universal_mcp/applications/zenquotes/app.py +3 -3
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/METADATA +4 -2
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/RECORD +115 -119
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/WHEEL +1 -1
- universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
- universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc16.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import os
|
|
3
|
-
from
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
import requests
|
|
4
5
|
|
|
5
6
|
from loguru import logger
|
|
6
|
-
from universal_mcp.applications.application import APIApplication
|
|
7
|
+
from universal_mcp.applications.application import APIApplication, BaseApplication
|
|
7
8
|
from universal_mcp.integrations import Integration
|
|
8
9
|
|
|
9
10
|
|
|
@@ -19,29 +20,30 @@ class LinkedinApp(APIApplication):
|
|
|
19
20
|
Args:
|
|
20
21
|
integration: The integration configuration containing credentials and other settings.
|
|
21
22
|
It is expected that the integration provides the 'x-api-key'
|
|
22
|
-
via headers in `integration.
|
|
23
|
+
via headers in `integration.get_credentials_async()`, e.g.,
|
|
23
24
|
`{"headers": {"x-api-key": "YOUR_API_KEY"}}`.
|
|
24
25
|
"""
|
|
25
26
|
super().__init__(name="linkedin", integration=integration)
|
|
26
|
-
|
|
27
27
|
self._base_url = None
|
|
28
|
-
self.
|
|
28
|
+
self._account_id = None
|
|
29
|
+
|
|
30
|
+
async def _get_account_id(self) -> str | None:
|
|
31
|
+
if self._account_id:
|
|
32
|
+
return self._account_id
|
|
29
33
|
if self.integration:
|
|
30
|
-
credentials = self.integration.
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
credentials = await self.integration.get_credentials_async()
|
|
35
|
+
self._account_id = credentials.get("account_id")
|
|
36
|
+
else:
|
|
37
|
+
logger.warning("Integration not found")
|
|
38
|
+
return self._account_id
|
|
33
39
|
|
|
34
40
|
@property
|
|
35
41
|
def base_url(self) -> str:
|
|
36
42
|
if not self._base_url:
|
|
37
43
|
unipile_dsn = os.getenv("UNIPILE_DSN")
|
|
38
44
|
if not unipile_dsn:
|
|
39
|
-
logger.error(
|
|
40
|
-
|
|
41
|
-
)
|
|
42
|
-
raise ValueError(
|
|
43
|
-
"UnipileApp: UNIPILE_DSN environment variable is required."
|
|
44
|
-
)
|
|
45
|
+
logger.error("UnipileApp: UNIPILE_DSN environment variable is not set.")
|
|
46
|
+
raise ValueError("UnipileApp: UNIPILE_DSN environment variable is required.")
|
|
45
47
|
self._base_url = f"https://{unipile_dsn}"
|
|
46
48
|
return self._base_url
|
|
47
49
|
|
|
@@ -56,31 +58,25 @@ class LinkedinApp(APIApplication):
|
|
|
56
58
|
Overrides the base class method to use X-Api-Key.
|
|
57
59
|
"""
|
|
58
60
|
if not self.integration:
|
|
59
|
-
logger.warning(
|
|
60
|
-
"UnipileApp: No integration configured, returning empty headers."
|
|
61
|
-
)
|
|
61
|
+
logger.warning("UnipileApp: No integration configured, returning empty headers.")
|
|
62
62
|
return {}
|
|
63
|
-
|
|
64
63
|
api_key = os.getenv("UNIPILE_API_KEY")
|
|
65
64
|
if not api_key:
|
|
66
|
-
logger.error(
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
return { # Or return minimal headers if some calls might not need auth (unlikely for Unipile)
|
|
70
|
-
"Content-Type": "application/json",
|
|
71
|
-
"Cache-Control": "no-cache",
|
|
72
|
-
}
|
|
73
|
-
|
|
65
|
+
logger.error("UnipileApp: API key not found in integration credentials for Unipile.")
|
|
66
|
+
return {"Content-Type": "application/json", "Cache-Control": "no-cache"}
|
|
74
67
|
logger.debug("UnipileApp: Using X-Api-Key for authentication.")
|
|
75
|
-
return {
|
|
76
|
-
"x-api-key": api_key,
|
|
77
|
-
"Content-Type": "application/json",
|
|
78
|
-
"Cache-Control": "no-cache", # Often good practice for APIs
|
|
79
|
-
}
|
|
68
|
+
return {"x-api-key": api_key, "Content-Type": "application/json", "Cache-Control": "no-cache"}
|
|
80
69
|
|
|
81
|
-
def
|
|
70
|
+
async def _aget_headers(self) -> dict[str, str]:
|
|
71
|
+
"""
|
|
72
|
+
Get the headers for Unipile API requests asynchronously.
|
|
73
|
+
Overrides the base class method to use X-Api-Key.
|
|
82
74
|
"""
|
|
83
|
-
|
|
75
|
+
return self._get_headers()
|
|
76
|
+
|
|
77
|
+
async def _aget_search_parameter_id(self, param_type: str, keywords: str) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Retrieves the ID for a given LinkedIn search parameter by its name asynchronously.
|
|
84
80
|
|
|
85
81
|
Args:
|
|
86
82
|
param_type: The type of parameter to search for (e.g., "LOCATION", "COMPANY").
|
|
@@ -94,29 +90,52 @@ class LinkedinApp(APIApplication):
|
|
|
94
90
|
httpx.HTTPError: If the API request fails.
|
|
95
91
|
"""
|
|
96
92
|
url = f"{self.base_url}/api/v1/linkedin/search/parameters"
|
|
97
|
-
params = {
|
|
98
|
-
|
|
99
|
-
"keywords": keywords,
|
|
100
|
-
"type": param_type,
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
response = self._get(url, params=params)
|
|
93
|
+
params = {"account_id": await self._get_account_id(), "keywords": keywords, "type": param_type}
|
|
94
|
+
response = await self._aget(url, params=params)
|
|
104
95
|
results = self._handle_response(response)
|
|
105
|
-
|
|
106
96
|
items = results.get("items", [])
|
|
107
97
|
if items:
|
|
108
|
-
# Return the ID of the first result, assuming it's the most relevant
|
|
109
98
|
return items[0]["id"]
|
|
110
|
-
|
|
111
99
|
raise ValueError(f'Could not find a matching ID for {param_type}: "{keywords}"')
|
|
112
100
|
|
|
113
|
-
def
|
|
101
|
+
async def start_new_chat(self, provider_id: str, text: str) -> dict[str, Any]:
|
|
102
|
+
"""
|
|
103
|
+
Starts a new chat conversation with a specified user by sending an initial message.
|
|
104
|
+
This function constructs a multipart/form-data request using the `files` parameter
|
|
105
|
+
to ensure correct formatting and headers, working around potential issues in the
|
|
106
|
+
underlying request method.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
provider_id: The LinkedIn provider ID of the user to start the chat with.
|
|
110
|
+
This is available in the response of the `retrieve_user_profile` tool.
|
|
111
|
+
text: The initial message content. For LinkedIn Recruiter accounts, this can include
|
|
112
|
+
HTML tags like <strong>, <em>, <a>, <ul>, <ol>, and <li>.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
A dictionary containing the details of the newly created chat.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
httpx.HTTPError: If the API request fails.
|
|
119
|
+
|
|
120
|
+
Tags:
|
|
121
|
+
linkedin, chat, create, start, new, messaging, api, important
|
|
122
|
+
"""
|
|
123
|
+
url = f"{self.base_url}/api/v1/chats"
|
|
124
|
+
form_payload = {"account_id": (None, await self._get_account_id()), "text": (None, text), "attendees_ids": (None, provider_id)}
|
|
125
|
+
api_key = os.getenv("UNIPILE_API_KEY")
|
|
126
|
+
if not api_key:
|
|
127
|
+
raise ValueError("UNIPILE_API_KEY environment variable is not set.")
|
|
128
|
+
headers = {"x-api-key": api_key}
|
|
129
|
+
response = requests.post(url, files=form_payload, headers=headers)
|
|
130
|
+
return self._handle_response(response)
|
|
131
|
+
|
|
132
|
+
async def list_all_chats(
|
|
114
133
|
self,
|
|
115
134
|
unread: bool | None = None,
|
|
116
135
|
cursor: str | None = None,
|
|
117
|
-
before: str | None = None,
|
|
118
|
-
after: str | None = None,
|
|
119
|
-
limit: int | None = None,
|
|
136
|
+
before: str | None = None,
|
|
137
|
+
after: str | None = None,
|
|
138
|
+
limit: int | None = None,
|
|
120
139
|
account_type: str | None = None,
|
|
121
140
|
) -> dict[str, Any]:
|
|
122
141
|
"""
|
|
@@ -141,9 +160,7 @@ class LinkedinApp(APIApplication):
|
|
|
141
160
|
"""
|
|
142
161
|
url = f"{self.base_url}/api/v1/chats"
|
|
143
162
|
params: dict[str, Any] = {}
|
|
144
|
-
|
|
145
|
-
params["account_id"] = self.account_id
|
|
146
|
-
|
|
163
|
+
params["account_id"] = await self._get_account_id()
|
|
147
164
|
if unread is not None:
|
|
148
165
|
params["unread"] = unread
|
|
149
166
|
if cursor:
|
|
@@ -156,18 +173,16 @@ class LinkedinApp(APIApplication):
|
|
|
156
173
|
params["limit"] = limit
|
|
157
174
|
if account_type:
|
|
158
175
|
params["account_type"] = account_type
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
response = self._get(url, params=params)
|
|
162
|
-
return response.json()
|
|
176
|
+
response = await self._aget(url, params=params)
|
|
177
|
+
return self._handle_response(response)
|
|
163
178
|
|
|
164
|
-
def list_chat_messages(
|
|
179
|
+
async def list_chat_messages(
|
|
165
180
|
self,
|
|
166
181
|
chat_id: str,
|
|
167
182
|
cursor: str | None = None,
|
|
168
|
-
before: str | None = None,
|
|
169
|
-
after: str | None = None,
|
|
170
|
-
limit: int | None = None,
|
|
183
|
+
before: str | None = None,
|
|
184
|
+
after: str | None = None,
|
|
185
|
+
limit: int | None = None,
|
|
171
186
|
sender_id: str | None = None,
|
|
172
187
|
) -> dict[str, Any]:
|
|
173
188
|
"""
|
|
@@ -202,15 +217,10 @@ class LinkedinApp(APIApplication):
|
|
|
202
217
|
params["limit"] = limit
|
|
203
218
|
if sender_id:
|
|
204
219
|
params["sender_id"] = sender_id
|
|
220
|
+
response = await self._aget(url, params=params)
|
|
221
|
+
return self._handle_response(response)
|
|
205
222
|
|
|
206
|
-
|
|
207
|
-
return response.json()
|
|
208
|
-
|
|
209
|
-
def send_chat_message(
|
|
210
|
-
self,
|
|
211
|
-
chat_id: str,
|
|
212
|
-
text: str,
|
|
213
|
-
) -> dict[str, Any]:
|
|
223
|
+
async def send_chat_message(self, chat_id: str, text: str) -> dict[str, Any]:
|
|
214
224
|
"""
|
|
215
225
|
Sends a text message to a specific chat conversation using its `chat_id`. This function creates a new message via a POST request, distinguishing it from read-only functions like `list_chat_messages`. It returns the API's response, which typically confirms the successful creation of the message.
|
|
216
226
|
|
|
@@ -230,11 +240,10 @@ class LinkedinApp(APIApplication):
|
|
|
230
240
|
"""
|
|
231
241
|
url = f"{self.base_url}/api/v1/chats/{chat_id}/messages"
|
|
232
242
|
payload: dict[str, Any] = {"text": text}
|
|
243
|
+
response = await self._apost(url, data=payload)
|
|
244
|
+
return self._handle_response(response)
|
|
233
245
|
|
|
234
|
-
|
|
235
|
-
return response.json()
|
|
236
|
-
|
|
237
|
-
def retrieve_chat(self, chat_id: str) -> dict[str, Any]:
|
|
246
|
+
async def retrieve_chat(self, chat_id: str) -> dict[str, Any]:
|
|
238
247
|
"""
|
|
239
248
|
Retrieves a single chat's details using its Unipile or provider-specific ID. This function is distinct from `list_all_chats`, which returns a collection, by targeting one specific conversation.
|
|
240
249
|
|
|
@@ -252,18 +261,17 @@ class LinkedinApp(APIApplication):
|
|
|
252
261
|
"""
|
|
253
262
|
url = f"{self.base_url}/api/v1/chats/{chat_id}"
|
|
254
263
|
params: dict[str, Any] = {}
|
|
255
|
-
if self.
|
|
256
|
-
params["account_id"] = self.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return response.json()
|
|
264
|
+
if await self._get_account_id():
|
|
265
|
+
params["account_id"] = await self._get_account_id()
|
|
266
|
+
response = await self._aget(url, params=params)
|
|
267
|
+
return self._handle_response(response)
|
|
260
268
|
|
|
261
|
-
def list_all_messages(
|
|
269
|
+
async def list_all_messages(
|
|
262
270
|
self,
|
|
263
271
|
cursor: str | None = None,
|
|
264
|
-
before: str | None = None,
|
|
265
|
-
after: str | None = None,
|
|
266
|
-
limit: int | None = None,
|
|
272
|
+
before: str | None = None,
|
|
273
|
+
after: str | None = None,
|
|
274
|
+
limit: int | None = None,
|
|
267
275
|
sender_id: str | None = None,
|
|
268
276
|
) -> dict[str, Any]:
|
|
269
277
|
"""
|
|
@@ -297,27 +305,22 @@ class LinkedinApp(APIApplication):
|
|
|
297
305
|
params["limit"] = limit
|
|
298
306
|
if sender_id:
|
|
299
307
|
params["sender_id"] = sender_id
|
|
300
|
-
if self.
|
|
301
|
-
params["account_id"] = self.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return response.json()
|
|
308
|
+
if await self._get_account_id():
|
|
309
|
+
params["account_id"] = await self._get_account_id()
|
|
310
|
+
response = await self._aget(url, params=params)
|
|
311
|
+
return self._handle_response(response)
|
|
305
312
|
|
|
306
|
-
def list_profile_posts(
|
|
307
|
-
self,
|
|
308
|
-
identifier: str, # User or Company provider internal ID
|
|
309
|
-
cursor: str | None = None,
|
|
310
|
-
limit: int | None = None, # 1-100 (spec says max 250)
|
|
311
|
-
is_company: bool | None = None,
|
|
313
|
+
async def list_profile_posts(
|
|
314
|
+
self, provider_id: str, cursor: str | None = None, limit: int | None = None, is_company: bool | None = None
|
|
312
315
|
) -> dict[str, Any]:
|
|
313
316
|
"""
|
|
314
317
|
Retrieves a paginated list of posts from a specific user or company profile using their provider ID. An authorizing `account_id` is required, and the `is_company` flag must specify the entity type, distinguishing this from `retrieve_post` which fetches a single post by its own ID.
|
|
315
318
|
|
|
316
319
|
Args:
|
|
317
|
-
|
|
320
|
+
provider_id: The entity's provider internal ID (LinkedIn ID).
|
|
318
321
|
cursor: Pagination cursor.
|
|
319
|
-
limit: Number of items to return (1-100
|
|
320
|
-
is_company: Boolean indicating if the
|
|
322
|
+
limit: Number of items to return (1-100).
|
|
323
|
+
is_company: Boolean indicating if the provider_id is for a company.
|
|
321
324
|
|
|
322
325
|
Returns:
|
|
323
326
|
A dictionary containing a list of post objects and pagination details.
|
|
@@ -328,19 +331,45 @@ class LinkedinApp(APIApplication):
|
|
|
328
331
|
Tags:
|
|
329
332
|
linkedin, post, list, user_posts, company_posts, content, api, important
|
|
330
333
|
"""
|
|
331
|
-
url = f"{self.base_url}/api/v1/users/{
|
|
332
|
-
params: dict[str, Any] = {"account_id": self.
|
|
334
|
+
url = f"{self.base_url}/api/v1/users/{provider_id}/posts"
|
|
335
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
333
336
|
if cursor:
|
|
334
337
|
params["cursor"] = cursor
|
|
335
338
|
if limit:
|
|
336
339
|
params["limit"] = limit
|
|
337
340
|
if is_company is not None:
|
|
338
341
|
params["is_company"] = is_company
|
|
342
|
+
response = await self._aget(url, params=params)
|
|
343
|
+
return self._handle_response(response)
|
|
339
344
|
|
|
340
|
-
|
|
341
|
-
|
|
345
|
+
async def list_profile_comments(self, provider_id: str, limit: int | None = None, cursor: str | None = None) -> dict[str, Any]:
|
|
346
|
+
"""
|
|
347
|
+
Retrieves a list of comments made by a specific user using their provider ID.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
provider_id: The entity's provider internal ID (LinkedIn ID).
|
|
351
|
+
limit: Number of items to return (1-100).
|
|
352
|
+
cursor: Pagination cursor.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
A dictionary containing the list of comments.
|
|
356
|
+
|
|
357
|
+
Raises:
|
|
358
|
+
httpx.HTTPError: If the API request fails.
|
|
342
359
|
|
|
343
|
-
|
|
360
|
+
Tags:
|
|
361
|
+
linkedin, user, comments, list, content, api
|
|
362
|
+
"""
|
|
363
|
+
url = f"{self.base_url}/api/v1/users/{provider_id}/comments"
|
|
364
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
365
|
+
if cursor:
|
|
366
|
+
params["cursor"] = cursor
|
|
367
|
+
if limit:
|
|
368
|
+
params["limit"] = limit
|
|
369
|
+
response = await self._aget(url, params=params)
|
|
370
|
+
return self._handle_response(response)
|
|
371
|
+
|
|
372
|
+
async def retrieve_own_profile(self) -> dict[str, Any]:
|
|
344
373
|
"""
|
|
345
374
|
Retrieves the profile details for the user associated with the Unipile account. This function targets the API's 'me' endpoint to fetch the authenticated user's profile, distinct from `retrieve_user_profile` which fetches profiles of other users by their public identifier.
|
|
346
375
|
|
|
@@ -354,11 +383,11 @@ class LinkedinApp(APIApplication):
|
|
|
354
383
|
linkedin, user, profile, me, retrieve, get, api
|
|
355
384
|
"""
|
|
356
385
|
url = f"{self.base_url}/api/v1/users/me"
|
|
357
|
-
params: dict[str, Any] = {"account_id": self.
|
|
358
|
-
response = self.
|
|
359
|
-
return
|
|
386
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
387
|
+
response = await self._aget(url, params=params)
|
|
388
|
+
return self._handle_response(response)
|
|
360
389
|
|
|
361
|
-
def retrieve_post(self, post_id: str) -> dict[str, Any]:
|
|
390
|
+
async def retrieve_post(self, post_id: str) -> dict[str, Any]:
|
|
362
391
|
"""
|
|
363
392
|
Fetches a specific post's details by its unique ID. Unlike `list_profile_posts`, which retrieves a collection of posts from a user or company profile, this function targets one specific post and returns its full object.
|
|
364
393
|
|
|
@@ -375,22 +404,18 @@ class LinkedinApp(APIApplication):
|
|
|
375
404
|
linkedin, post, retrieve, get, content, api, important
|
|
376
405
|
"""
|
|
377
406
|
url = f"{self.base_url}/api/v1/posts/{post_id}"
|
|
378
|
-
params: dict[str, Any] = {"account_id": self.
|
|
379
|
-
response = self.
|
|
380
|
-
return
|
|
407
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
408
|
+
response = await self._aget(url, params=params)
|
|
409
|
+
return self._handle_response(response)
|
|
381
410
|
|
|
382
|
-
def list_post_comments(
|
|
383
|
-
self,
|
|
384
|
-
post_id: str,
|
|
385
|
-
comment_id: str | None = None,
|
|
386
|
-
cursor: str | None = None,
|
|
387
|
-
limit: int | None = None,
|
|
411
|
+
async def list_post_comments(
|
|
412
|
+
self, post_id: str, comment_id: str | None = None, cursor: str | None = None, limit: int | None = None
|
|
388
413
|
) -> dict[str, Any]:
|
|
389
414
|
"""
|
|
390
|
-
Fetches comments for a specific post. Providing an optional `comment_id` retrieves threaded replies instead of top-level comments.
|
|
415
|
+
Fetches comments for a specific post. Providing an optional `comment_id` retrieves threaded replies instead of top-level comments. `retrieve_post` or `list_profile_posts` can be used to obtain the `post_id` which is the social_id in their response.
|
|
391
416
|
|
|
392
417
|
Args:
|
|
393
|
-
post_id: The social ID of the post.
|
|
418
|
+
post_id: The social ID of the post which you get from using `retrieve_post` or `list_profile_posts` tools.
|
|
394
419
|
comment_id: If provided, retrieves replies to this comment ID instead of top-level comments.
|
|
395
420
|
cursor: Pagination cursor.
|
|
396
421
|
limit: Number of comments to return. (OpenAPI spec shows type string, passed as string if provided).
|
|
@@ -405,22 +430,18 @@ class LinkedinApp(APIApplication):
|
|
|
405
430
|
linkedin, post, comment, list, content, api, important
|
|
406
431
|
"""
|
|
407
432
|
url = f"{self.base_url}/api/v1/posts/{post_id}/comments"
|
|
408
|
-
params: dict[str, Any] = {"account_id": self.
|
|
433
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
409
434
|
if cursor:
|
|
410
435
|
params["cursor"] = cursor
|
|
411
436
|
if limit is not None:
|
|
412
437
|
params["limit"] = str(limit)
|
|
413
438
|
if comment_id:
|
|
414
439
|
params["comment_id"] = comment_id
|
|
440
|
+
response = await self._aget(url, params=params)
|
|
441
|
+
return self._handle_response(response)
|
|
415
442
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
def create_post(
|
|
420
|
-
self,
|
|
421
|
-
text: str,
|
|
422
|
-
mentions: list[dict[str, Any]] | None = None,
|
|
423
|
-
external_link: str | None = None,
|
|
443
|
+
async def create_post(
|
|
444
|
+
self, text: str, mentions: list[dict[str, Any]] | None = None, external_link: str | None = None
|
|
424
445
|
) -> dict[str, Any]:
|
|
425
446
|
"""
|
|
426
447
|
Publishes a new top-level post from the account, including text, user mentions, and an external link. This function creates original content, distinguishing it from `create_post_comment` which adds replies to existing posts.
|
|
@@ -441,26 +462,16 @@ class LinkedinApp(APIApplication):
|
|
|
441
462
|
linkedin, post, create, share, content, api, important
|
|
442
463
|
"""
|
|
443
464
|
url = f"{self.base_url}/api/v1/posts"
|
|
444
|
-
|
|
445
|
-
params: dict[str, str] = {
|
|
446
|
-
"account_id": self.account_id,
|
|
447
|
-
"text": text,
|
|
448
|
-
}
|
|
449
|
-
|
|
465
|
+
params: dict[str, str] = {"account_id": await self._get_account_id(), "text": text}
|
|
450
466
|
if mentions:
|
|
451
467
|
params["mentions"] = mentions
|
|
452
468
|
if external_link:
|
|
453
469
|
params["external_link"] = external_link
|
|
470
|
+
response = await self._apost(url, data=params)
|
|
471
|
+
return self._handle_response(response)
|
|
454
472
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
def list_content_reactions(
|
|
459
|
-
self,
|
|
460
|
-
post_id: str,
|
|
461
|
-
comment_id: str | None = None,
|
|
462
|
-
cursor: str | None = None,
|
|
463
|
-
limit: int | None = None,
|
|
473
|
+
async def list_content_reactions(
|
|
474
|
+
self, post_id: str, comment_id: str | None = None, cursor: str | None = None, limit: int | None = None
|
|
464
475
|
) -> dict[str, Any]:
|
|
465
476
|
"""
|
|
466
477
|
Retrieves a paginated list of reactions for a given post or, optionally, a specific comment. This read-only operation uses the account for the request, distinguishing it from the `create_reaction` function which adds new reactions.
|
|
@@ -469,7 +480,7 @@ class LinkedinApp(APIApplication):
|
|
|
469
480
|
post_id: The social ID of the post.
|
|
470
481
|
comment_id: If provided, retrieves reactions for this comment ID.
|
|
471
482
|
cursor: Pagination cursor.
|
|
472
|
-
limit: Number of reactions to return (1-100
|
|
483
|
+
limit: Number of reactions to return (1-100).
|
|
473
484
|
|
|
474
485
|
Returns:
|
|
475
486
|
A dictionary containing a list of reaction objects and pagination details.
|
|
@@ -481,23 +492,18 @@ class LinkedinApp(APIApplication):
|
|
|
481
492
|
linkedin, post, reaction, list, like, content, api
|
|
482
493
|
"""
|
|
483
494
|
url = f"{self.base_url}/api/v1/posts/{post_id}/reactions"
|
|
484
|
-
params: dict[str, Any] = {"account_id": self.
|
|
495
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
485
496
|
if cursor:
|
|
486
497
|
params["cursor"] = cursor
|
|
487
498
|
if limit:
|
|
488
499
|
params["limit"] = limit
|
|
489
500
|
if comment_id:
|
|
490
501
|
params["comment_id"] = comment_id
|
|
502
|
+
response = await self._aget(url, params=params)
|
|
503
|
+
return self._handle_response(response)
|
|
491
504
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
def create_post_comment(
|
|
496
|
-
self,
|
|
497
|
-
post_social_id: str,
|
|
498
|
-
text: str,
|
|
499
|
-
comment_id: str | None = None, # If provided, replies to a specific comment
|
|
500
|
-
mentions_body: list[dict[str, Any]] | None = None,
|
|
505
|
+
async def create_post_comment(
|
|
506
|
+
self, post_social_id: str, text: str, comment_id: str | None = None, mentions_body: list[dict[str, Any]] | None = None
|
|
501
507
|
) -> dict[str, Any]:
|
|
502
508
|
"""
|
|
503
509
|
Publishes a comment on a specified post. By providing an optional `comment_id`, it creates a threaded reply to an existing comment instead of a new top-level one. This function's dual capability distinguishes it from `list_post_comments`, which only retrieves comments and their replies.
|
|
@@ -519,33 +525,18 @@ class LinkedinApp(APIApplication):
|
|
|
519
525
|
linkedin, post, comment, create, content, api, important
|
|
520
526
|
"""
|
|
521
527
|
url = f"{self.base_url}/api/v1/posts/{post_social_id}/comments"
|
|
522
|
-
params: dict[str, Any] = {
|
|
523
|
-
"account_id": self.account_id,
|
|
524
|
-
"text": text,
|
|
525
|
-
}
|
|
526
|
-
|
|
528
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id(), "text": text}
|
|
527
529
|
if comment_id:
|
|
528
530
|
params["comment_id"] = comment_id
|
|
529
|
-
|
|
530
531
|
if mentions_body:
|
|
531
532
|
params = {"mentions": mentions_body}
|
|
533
|
+
response = await self._apost(url, data=params)
|
|
534
|
+
return self._handle_response(response)
|
|
532
535
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
try:
|
|
536
|
-
return response.json()
|
|
537
|
-
except json.JSONDecodeError:
|
|
538
|
-
return {
|
|
539
|
-
"status": response.status_code,
|
|
540
|
-
"message": "Comment action processed.",
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
def create_reaction(
|
|
536
|
+
async def create_reaction(
|
|
544
537
|
self,
|
|
545
538
|
post_social_id: str,
|
|
546
|
-
reaction_type: Literal[
|
|
547
|
-
"like", "celebrate", "love", "insightful", "funny", "support"
|
|
548
|
-
],
|
|
539
|
+
reaction_type: Literal["like", "celebrate", "love", "insightful", "funny", "support"],
|
|
549
540
|
comment_id: str | None = None,
|
|
550
541
|
) -> dict[str, Any]:
|
|
551
542
|
"""
|
|
@@ -566,32 +557,18 @@ class LinkedinApp(APIApplication):
|
|
|
566
557
|
linkedin, post, reaction, create, like, content, api, important
|
|
567
558
|
"""
|
|
568
559
|
url = f"{self.base_url}/api/v1/posts/reaction"
|
|
569
|
-
|
|
570
|
-
params: dict[str, str] = {
|
|
571
|
-
"account_id": self.account_id,
|
|
572
|
-
"post_id": post_social_id,
|
|
573
|
-
"reaction_type": reaction_type,
|
|
574
|
-
}
|
|
575
|
-
|
|
560
|
+
params: dict[str, str] = {"account_id": await self._get_account_id(), "post_id": post_social_id, "reaction_type": reaction_type}
|
|
576
561
|
if comment_id:
|
|
577
562
|
params["comment_id"] = comment_id
|
|
563
|
+
response = await self._apost(url, data=params)
|
|
564
|
+
return self._handle_response(response)
|
|
578
565
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
try:
|
|
582
|
-
return response.json()
|
|
583
|
-
except json.JSONDecodeError:
|
|
584
|
-
return {
|
|
585
|
-
"status": response.status_code,
|
|
586
|
-
"message": "Reaction action processed.",
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
def retrieve_user_profile(self, identifier: str) -> dict[str, Any]:
|
|
566
|
+
async def retrieve_user_profile(self, public_identifier: str) -> dict[str, Any]:
|
|
590
567
|
"""
|
|
591
568
|
Retrieves a specific LinkedIn user's profile using their public or internal ID. Unlike `retrieve_own_profile`, which fetches the authenticated user's details, this function targets and returns data for any specified third-party user profile on the platform.
|
|
592
569
|
|
|
593
570
|
Args:
|
|
594
|
-
|
|
571
|
+
public_identifier: Extract this value from the response of `search_people` tool. The response contains a public_identifier field.For example, for https://www.linkedin.com/in/manojbajaj95/, the identifier is "manojbajaj95".
|
|
595
572
|
|
|
596
573
|
Returns:
|
|
597
574
|
A dictionary containing the user's profile details.
|
|
@@ -602,12 +579,12 @@ class LinkedinApp(APIApplication):
|
|
|
602
579
|
Tags:
|
|
603
580
|
linkedin, user, profile, retrieve, get, api, important
|
|
604
581
|
"""
|
|
605
|
-
url = f"{self.base_url}/api/v1/users/{
|
|
606
|
-
params: dict[str, Any] = {"account_id": self.
|
|
607
|
-
response = self.
|
|
582
|
+
url = f"{self.base_url}/api/v1/users/{public_identifier}"
|
|
583
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
584
|
+
response = await self._aget(url, params=params)
|
|
608
585
|
return self._handle_response(response)
|
|
609
586
|
|
|
610
|
-
def search_people(
|
|
587
|
+
async def search_people(
|
|
611
588
|
self,
|
|
612
589
|
cursor: str | None = None,
|
|
613
590
|
limit: int | None = None,
|
|
@@ -618,7 +595,7 @@ class LinkedinApp(APIApplication):
|
|
|
618
595
|
) -> dict[str, Any]:
|
|
619
596
|
"""
|
|
620
597
|
Searches for LinkedIn user profiles using keywords, with optional filters for location, industry, and company. This function specifically targets the 'people' category, distinguishing it from other search methods like `search_companies` or `search_jobs` that query different entity types through the same API endpoint.
|
|
621
|
-
|
|
598
|
+
|
|
622
599
|
Args:
|
|
623
600
|
cursor: Pagination cursor for the next page of entries.
|
|
624
601
|
limit: Number of items to return (up to 50 for Classic search).
|
|
@@ -626,42 +603,35 @@ class LinkedinApp(APIApplication):
|
|
|
626
603
|
location: The geographical location to filter people by (e.g., "United States").
|
|
627
604
|
industry: The industry to filter people by.(eg., "Information Technology and Services").
|
|
628
605
|
company: The company to filter people by.(e.g., "Google").
|
|
629
|
-
|
|
606
|
+
|
|
630
607
|
Returns:
|
|
631
608
|
A dictionary containing search results and pagination details.
|
|
632
|
-
|
|
609
|
+
|
|
633
610
|
Raises:
|
|
634
611
|
httpx.HTTPError: If the API request fails.
|
|
635
612
|
"""
|
|
636
613
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
637
|
-
|
|
638
|
-
params: dict[str, Any] = {"account_id": self.account_id}
|
|
614
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
639
615
|
if cursor:
|
|
640
616
|
params["cursor"] = cursor
|
|
641
617
|
if limit is not None:
|
|
642
618
|
params["limit"] = limit
|
|
643
|
-
|
|
644
619
|
payload: dict[str, Any] = {"api": "classic", "category": "people"}
|
|
645
|
-
|
|
646
620
|
if keywords:
|
|
647
621
|
payload["keywords"] = keywords
|
|
648
|
-
|
|
649
622
|
if location:
|
|
650
|
-
location_id = self.
|
|
623
|
+
location_id = await self._aget_search_parameter_id("LOCATION", location)
|
|
651
624
|
payload["location"] = [location_id]
|
|
652
|
-
|
|
653
625
|
if industry:
|
|
654
|
-
industry_id = self.
|
|
626
|
+
industry_id = await self._aget_search_parameter_id("INDUSTRY", industry)
|
|
655
627
|
payload["industry"] = [industry_id]
|
|
656
|
-
|
|
657
628
|
if company:
|
|
658
|
-
company_id = self.
|
|
629
|
+
company_id = await self._aget_search_parameter_id("COMPANY", company)
|
|
659
630
|
payload["company"] = [company_id]
|
|
660
|
-
|
|
661
|
-
response = self._post(url, params=params, data=payload)
|
|
631
|
+
response = await self._apost(url, params=params, data=payload)
|
|
662
632
|
return self._handle_response(response)
|
|
663
633
|
|
|
664
|
-
def search_companies(
|
|
634
|
+
async def search_companies(
|
|
665
635
|
self,
|
|
666
636
|
cursor: str | None = None,
|
|
667
637
|
limit: int | None = None,
|
|
@@ -671,45 +641,39 @@ class LinkedinApp(APIApplication):
|
|
|
671
641
|
) -> dict[str, Any]:
|
|
672
642
|
"""
|
|
673
643
|
Performs a paginated search for companies on LinkedIn using keywords, with optional location and industry filters. Its specific 'companies' search category distinguishes it from other methods like `search_people` or `search_posts`, ensuring that only company profiles are returned.
|
|
674
|
-
|
|
644
|
+
|
|
675
645
|
Args:
|
|
676
646
|
cursor: Pagination cursor for the next page of entries.
|
|
677
647
|
limit: Number of items to return (up to 50 for Classic search).
|
|
678
648
|
keywords: Keywords to search for.
|
|
679
649
|
location: The geographical location to filter companies by (e.g., "United States").
|
|
680
650
|
industry: The industry to filter companies by.(e.g., "Information Technology and Services").
|
|
681
|
-
|
|
651
|
+
|
|
682
652
|
Returns:
|
|
683
653
|
A dictionary containing search results and pagination details.
|
|
684
|
-
|
|
654
|
+
|
|
685
655
|
Raises:
|
|
686
656
|
httpx.HTTPError: If the API request fails.
|
|
687
657
|
"""
|
|
688
658
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
689
|
-
|
|
690
|
-
params: dict[str, Any] = {"account_id": self.account_id}
|
|
659
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
691
660
|
if cursor:
|
|
692
661
|
params["cursor"] = cursor
|
|
693
662
|
if limit is not None:
|
|
694
663
|
params["limit"] = limit
|
|
695
|
-
|
|
696
664
|
payload: dict[str, Any] = {"api": "classic", "category": "companies"}
|
|
697
|
-
|
|
698
665
|
if keywords:
|
|
699
666
|
payload["keywords"] = keywords
|
|
700
|
-
|
|
701
667
|
if location:
|
|
702
|
-
location_id = self.
|
|
668
|
+
location_id = await self._aget_search_parameter_id("LOCATION", location)
|
|
703
669
|
payload["location"] = [location_id]
|
|
704
|
-
|
|
705
670
|
if industry:
|
|
706
|
-
industry_id = self.
|
|
671
|
+
industry_id = await self._aget_search_parameter_id("INDUSTRY", industry)
|
|
707
672
|
payload["industry"] = [industry_id]
|
|
708
|
-
|
|
709
|
-
response = self._post(url, params=params, data=payload)
|
|
673
|
+
response = await self._apost(url, params=params, data=payload)
|
|
710
674
|
return self._handle_response(response)
|
|
711
675
|
|
|
712
|
-
def search_posts(
|
|
676
|
+
async def search_posts(
|
|
713
677
|
self,
|
|
714
678
|
cursor: str | None = None,
|
|
715
679
|
limit: int | None = None,
|
|
@@ -719,111 +683,609 @@ class LinkedinApp(APIApplication):
|
|
|
719
683
|
) -> dict[str, Any]:
|
|
720
684
|
"""
|
|
721
685
|
Performs a keyword-based search for LinkedIn posts, allowing filters for date and sorting by relevance. This function executes a general, platform-wide content search, distinguishing it from other search functions that target people, companies, or jobs, and from `list_profile_posts` which retrieves from a specific profile.
|
|
722
|
-
|
|
686
|
+
|
|
723
687
|
Args:
|
|
724
688
|
cursor: Pagination cursor for the next page of entries.
|
|
725
689
|
limit: Number of items to return (up to 50 for Classic search).
|
|
726
690
|
keywords: Keywords to search for.
|
|
727
691
|
date_posted: Filter by when the post was posted.
|
|
728
692
|
sort_by: How to sort the results.
|
|
729
|
-
|
|
693
|
+
|
|
730
694
|
Returns:
|
|
731
695
|
A dictionary containing search results and pagination details.
|
|
732
|
-
|
|
696
|
+
|
|
733
697
|
Raises:
|
|
734
698
|
httpx.HTTPError: If the API request fails.
|
|
735
699
|
"""
|
|
736
700
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
737
|
-
|
|
738
|
-
params: dict[str, Any] = {"account_id": self.account_id}
|
|
701
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
739
702
|
if cursor:
|
|
740
703
|
params["cursor"] = cursor
|
|
741
704
|
if limit is not None:
|
|
742
705
|
params["limit"] = limit
|
|
743
|
-
|
|
744
706
|
payload: dict[str, Any] = {"api": "classic", "category": "posts"}
|
|
745
|
-
|
|
746
707
|
if keywords:
|
|
747
708
|
payload["keywords"] = keywords
|
|
748
709
|
if date_posted:
|
|
749
710
|
payload["date_posted"] = date_posted
|
|
750
711
|
if sort_by:
|
|
751
712
|
payload["sort_by"] = sort_by
|
|
752
|
-
|
|
753
|
-
response = self._post(url, params=params, data=payload)
|
|
713
|
+
response = await self._apost(url, params=params, data=payload)
|
|
754
714
|
return self._handle_response(response)
|
|
755
715
|
|
|
756
|
-
def search_jobs(
|
|
716
|
+
async def search_jobs(
|
|
757
717
|
self,
|
|
758
718
|
cursor: str | None = None,
|
|
759
719
|
limit: int | None = None,
|
|
760
720
|
keywords: str | None = None,
|
|
761
721
|
region: str | None = None,
|
|
762
722
|
sort_by: Literal["relevance", "date"] = "relevance",
|
|
763
|
-
minimum_salary_value:
|
|
723
|
+
minimum_salary_value: Literal[40, 60, 80, 100, 120, 140, 160, 180, 200] = 40,
|
|
764
724
|
industry: str | None = None,
|
|
765
725
|
) -> dict[str, Any]:
|
|
766
726
|
"""
|
|
767
727
|
Performs a LinkedIn search for jobs, filtering results by keywords, region, industry, and minimum salary. Unlike other search functions (`search_people`, `search_companies`), this method is specifically configured to query the 'jobs' category, providing a paginated list of relevant employment opportunities.
|
|
768
|
-
|
|
728
|
+
|
|
769
729
|
Args:
|
|
770
730
|
cursor: Pagination cursor for the next page of entries.
|
|
771
731
|
limit: Number of items to return (up to 50 for Classic search).
|
|
772
732
|
keywords: Keywords to search for.
|
|
773
733
|
region: The geographical region to filter jobs by (e.g., "United States").
|
|
774
734
|
sort_by: How to sort the results.(e.g., "relevance" or "date".)
|
|
775
|
-
minimum_salary_value: The minimum salary to filter for.
|
|
735
|
+
minimum_salary_value: The minimum salary to filter for. Allowed values are 40, 60, 80, 100, 120, 140, 160, 180, 200.
|
|
776
736
|
industry: The industry to filter jobs by.(e.g., "Software Development").
|
|
777
|
-
|
|
737
|
+
|
|
778
738
|
Returns:
|
|
779
739
|
A dictionary containing search results and pagination details.
|
|
780
|
-
|
|
740
|
+
|
|
781
741
|
Raises:
|
|
782
742
|
httpx.HTTPError: If the API request fails.
|
|
783
743
|
ValueError: If the specified location is not found.
|
|
784
744
|
"""
|
|
785
745
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
786
|
-
|
|
787
|
-
params: dict[str, Any] = {"account_id": self.account_id}
|
|
746
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
788
747
|
if cursor:
|
|
789
748
|
params["cursor"] = cursor
|
|
790
749
|
if limit is not None:
|
|
791
750
|
params["limit"] = limit
|
|
792
|
-
|
|
793
751
|
payload: dict[str, Any] = {
|
|
794
752
|
"api": "classic",
|
|
795
753
|
"category": "jobs",
|
|
796
|
-
"minimum_salary": {
|
|
797
|
-
"currency": "USD",
|
|
798
|
-
"value": minimum_salary_value,
|
|
799
|
-
},
|
|
754
|
+
"minimum_salary": {"currency": "USD", "value": minimum_salary_value},
|
|
800
755
|
}
|
|
801
|
-
|
|
802
756
|
if keywords:
|
|
803
757
|
payload["keywords"] = keywords
|
|
804
758
|
if sort_by:
|
|
805
759
|
payload["sort_by"] = sort_by
|
|
806
|
-
|
|
807
|
-
# If location is provided, get its ID and add it to the payload
|
|
808
760
|
if region:
|
|
809
|
-
location_id = self.
|
|
761
|
+
location_id = await self._aget_search_parameter_id("LOCATION", region)
|
|
810
762
|
payload["region"] = location_id
|
|
811
|
-
|
|
812
763
|
if industry:
|
|
813
|
-
industry_id = self.
|
|
764
|
+
industry_id = await self._aget_search_parameter_id("INDUSTRY", industry)
|
|
814
765
|
payload["industry"] = [industry_id]
|
|
766
|
+
response = await self._apost(url, params=params, data=payload)
|
|
767
|
+
return self._handle_response(response)
|
|
768
|
+
|
|
769
|
+
async def send_invitation(self, provider_id: str, user_email: str | None = None, message: str | None = None) -> dict[str, Any]:
|
|
770
|
+
"""
|
|
771
|
+
Sends a connection invitation to a LinkedIn user specified by their provider ID. An optional message and the user's email can be included.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
provider_id: The LinkedIn provider ID of the user to invite. This is available in response of `retrieve_user_profile` tool.
|
|
775
|
+
user_email: Optional. The email address of the user, which may be required by LinkedIn.
|
|
776
|
+
message: Optional. A personalized message to include with the invitation (max 300 characters).
|
|
777
|
+
|
|
778
|
+
Returns:
|
|
779
|
+
A dictionary confirming the invitation was sent.
|
|
780
|
+
|
|
781
|
+
Raises:
|
|
782
|
+
httpx.HTTPError: If the API request fails.
|
|
783
|
+
ValueError: If the message exceeds 300 characters.
|
|
784
|
+
|
|
785
|
+
Tags:
|
|
786
|
+
linkedin, user, invite, connect, contact, api, important
|
|
787
|
+
"""
|
|
788
|
+
url = f"{self.base_url}/api/v1/users/invite"
|
|
789
|
+
payload: dict[str, Any] = {"account_id": await self._get_account_id(), "provider_id": provider_id}
|
|
790
|
+
if user_email:
|
|
791
|
+
payload["user_email"] = user_email
|
|
792
|
+
if message:
|
|
793
|
+
if len(message) > 300:
|
|
794
|
+
raise ValueError("Message cannot exceed 300 characters.")
|
|
795
|
+
payload["message"] = message
|
|
796
|
+
response = await self._apost(url, data=payload)
|
|
797
|
+
return self._handle_response(response)
|
|
798
|
+
|
|
799
|
+
async def list_sent_invitations(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
800
|
+
"""
|
|
801
|
+
Retrieves a paginated list of all sent connection invitations that are currently pending. This function allows for iterating through the history of outstanding connection requests made from the specified account.
|
|
802
|
+
|
|
803
|
+
Args:
|
|
804
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
805
|
+
limit: The number of items to return, ranging from 1 to 100. Defaults to 10 if not specified.
|
|
806
|
+
|
|
807
|
+
Returns:
|
|
808
|
+
A dictionary containing a list of sent invitation objects and pagination details.
|
|
809
|
+
|
|
810
|
+
Raises:
|
|
811
|
+
httpx.HTTPError: If the API request fails.
|
|
812
|
+
|
|
813
|
+
Tags:
|
|
814
|
+
linkedin, user, invite, sent, list, contacts, api
|
|
815
|
+
"""
|
|
816
|
+
url = f"{self.base_url}/api/v1/users/invite/sent"
|
|
817
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
818
|
+
if cursor:
|
|
819
|
+
params["cursor"] = cursor
|
|
820
|
+
if limit is not None:
|
|
821
|
+
params["limit"] = limit
|
|
822
|
+
response = await self._aget(url, params=params)
|
|
823
|
+
return self._handle_response(response)
|
|
824
|
+
|
|
825
|
+
async def list_received_invitations(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
826
|
+
"""
|
|
827
|
+
Retrieves a paginated list of all received connection invitations. This function allows for reviewing and processing incoming connection requests to the specified account.
|
|
828
|
+
|
|
829
|
+
Args:
|
|
830
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
831
|
+
limit: The number of items to return, ranging from 1 to 100. Defaults to 10 if not specified.
|
|
832
|
+
|
|
833
|
+
Returns:
|
|
834
|
+
A dictionary containing a list of received invitation objects and pagination details.
|
|
835
|
+
|
|
836
|
+
Raises:
|
|
837
|
+
httpx.HTTPError: If the API request fails.
|
|
838
|
+
|
|
839
|
+
Tags:
|
|
840
|
+
linkedin, user, invite, received, list, contacts, api
|
|
841
|
+
"""
|
|
842
|
+
url = f"{self.base_url}/api/v1/users/invite/received"
|
|
843
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
844
|
+
if cursor:
|
|
845
|
+
params["cursor"] = cursor
|
|
846
|
+
if limit is not None:
|
|
847
|
+
params["limit"] = limit
|
|
848
|
+
response = await self._aget(url, params=params)
|
|
849
|
+
return self._handle_response(response)
|
|
850
|
+
|
|
851
|
+
async def handle_received_invitation(
|
|
852
|
+
self, invitation_id: str, action: Literal["accept", "decline"], shared_secret: str
|
|
853
|
+
) -> dict[str, Any]:
|
|
854
|
+
"""
|
|
855
|
+
Accepts or declines a received LinkedIn connection invitation using its ID and a required shared secret. This function performs a POST request to update the invitation's status, distinguishing it from read-only functions like `list_received_invitations`.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
invitation_id: The ID of the invitation to handle.Get this ID from the 'list_received_invitations' tool.
|
|
859
|
+
action: The action to perform, either "accept" or "decline".
|
|
860
|
+
shared_secret: The token provided by LinkedIn, retrieved from the 'list_received_invitations' tool, which is mandatory for this action.
|
|
861
|
+
|
|
862
|
+
Returns:
|
|
863
|
+
A dictionary confirming the action was processed.
|
|
864
|
+
|
|
865
|
+
Raises:
|
|
866
|
+
httpx.HTTPError: If the API request fails.
|
|
867
|
+
|
|
868
|
+
Tags:
|
|
869
|
+
linkedin, user, invite, received, handle, accept, decline, api
|
|
870
|
+
"""
|
|
871
|
+
url = f"{self.base_url}/api/v1/users/invite/received/{invitation_id}"
|
|
872
|
+
payload: dict[str, Any] = {"provider": "LINKEDIN", "action": action, "shared_secret": shared_secret, "account_id": await self._get_account_id()}
|
|
873
|
+
response = await self._apost(url, data=payload)
|
|
874
|
+
return self._handle_response(response)
|
|
875
|
+
|
|
876
|
+
async def cancel_sent_invitation(self, invitation_id: str) -> dict[str, Any]:
|
|
877
|
+
"""
|
|
878
|
+
Cancels a sent LinkedIn connection invitation that is currently pending. This function performs a DELETE request to remove the invitation, withdrawing the connection request.
|
|
879
|
+
|
|
880
|
+
Args:
|
|
881
|
+
invitation_id: The unique ID of the invitation to cancel. This ID can be obtained from the 'list_sent_invitations' tool.
|
|
882
|
+
|
|
883
|
+
Returns:
|
|
884
|
+
A dictionary confirming the invitation was cancelled.
|
|
885
|
+
|
|
886
|
+
Raises:
|
|
887
|
+
httpx.HTTPError: If the API request fails.
|
|
888
|
+
|
|
889
|
+
Tags:
|
|
890
|
+
linkedin, user, invite, sent, cancel, delete, api
|
|
891
|
+
"""
|
|
892
|
+
url = f"{self.base_url}/api/v1/users/invite/sent/{invitation_id}"
|
|
893
|
+
params = {"account_id": await self._get_account_id()}
|
|
894
|
+
response = await self._adelete(url, params=params)
|
|
895
|
+
return self._handle_response(response)
|
|
896
|
+
|
|
897
|
+
async def list_followers(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
898
|
+
"""
|
|
899
|
+
Retrieves a paginated list of all followers for the current user's account. This function is distinct from `list_following` as it shows who follows the user, not who the user follows.
|
|
900
|
+
|
|
901
|
+
Args:
|
|
902
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
903
|
+
limit: The number of items to return, ranging from 1 to 1000.
|
|
904
|
+
|
|
905
|
+
Returns:
|
|
906
|
+
A dictionary containing a list of follower objects and pagination details.
|
|
907
|
+
|
|
908
|
+
Raises:
|
|
909
|
+
httpx.HTTPError: If the API request fails.
|
|
910
|
+
|
|
911
|
+
Tags:
|
|
912
|
+
linkedin, user, followers, list, contacts, api
|
|
913
|
+
"""
|
|
914
|
+
url = f"{self.base_url}/api/v1/users/followers"
|
|
915
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
916
|
+
if cursor:
|
|
917
|
+
params["cursor"] = cursor
|
|
918
|
+
if limit is not None:
|
|
919
|
+
params["limit"] = limit
|
|
920
|
+
response = await self._aget(url, params=params)
|
|
921
|
+
return self._handle_response(response)
|
|
922
|
+
|
|
923
|
+
async def list_following(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
924
|
+
"""
|
|
925
|
+
Retrieves a paginated list of all accounts that the current user is following. This function is the counterpart to `list_followers`, focusing on the user's outgoing connections rather than incoming ones.
|
|
926
|
+
|
|
927
|
+
Args:
|
|
928
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
929
|
+
limit: The number of items to return, ranging from 1 to 1000.
|
|
930
|
+
|
|
931
|
+
Returns:
|
|
932
|
+
A dictionary containing a list of followed account objects and pagination details.
|
|
933
|
+
|
|
934
|
+
Raises:
|
|
935
|
+
httpx.HTTPError: If the API request fails.
|
|
936
|
+
|
|
937
|
+
Tags:
|
|
938
|
+
linkedin, user, following, list, contacts, api
|
|
939
|
+
"""
|
|
940
|
+
url = f"{self.base_url}/api/v1/users/following"
|
|
941
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
942
|
+
if cursor:
|
|
943
|
+
params["cursor"] = cursor
|
|
944
|
+
if limit is not None:
|
|
945
|
+
params["limit"] = limit
|
|
946
|
+
response = self._get(url, params=params)
|
|
947
|
+
return self._handle_response(response)
|
|
948
|
+
|
|
949
|
+
async def list_job_postings(
|
|
950
|
+
self,
|
|
951
|
+
category: Literal["active", "draft", "closed"] = "active",
|
|
952
|
+
limit: int | None = None,
|
|
953
|
+
cursor: str | None = None,
|
|
954
|
+
) -> dict[str, Any]:
|
|
955
|
+
"""
|
|
956
|
+
Retrieve the job offers you have posted on LinkedIn whether they are open, closed, or still drafts.
|
|
957
|
+
|
|
958
|
+
Args:
|
|
959
|
+
category: The state of the requested job postings. Default is active.
|
|
960
|
+
limit: A limit for the number of items returned in the response. The value can be set between 1 and 250.
|
|
961
|
+
cursor: A cursor for pagination purposes.
|
|
962
|
+
|
|
963
|
+
Returns:
|
|
964
|
+
A dictionary containing a list of job postings and pagination details.
|
|
965
|
+
|
|
966
|
+
Raises:
|
|
967
|
+
httpx.HTTPError: If the API request fails.
|
|
968
|
+
|
|
969
|
+
Tags:
|
|
970
|
+
linkedin, jobs, list, postings, api
|
|
971
|
+
"""
|
|
972
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs"
|
|
973
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
974
|
+
if category:
|
|
975
|
+
params["category"] = category
|
|
976
|
+
if limit:
|
|
977
|
+
params["limit"] = limit
|
|
978
|
+
if cursor:
|
|
979
|
+
params["cursor"] = cursor
|
|
980
|
+
response = await self._aget(url, params=params)
|
|
981
|
+
return self._handle_response(response)
|
|
982
|
+
|
|
983
|
+
async def create_job_posting(
|
|
984
|
+
self,
|
|
985
|
+
job_title: dict[str, str],
|
|
986
|
+
company: dict[str, str],
|
|
987
|
+
workplace: Literal["ON_SITE", "HYBRID", "REMOTE"],
|
|
988
|
+
location: str,
|
|
989
|
+
description: str,
|
|
990
|
+
employment_status: Literal[
|
|
991
|
+
"FULL_TIME", "PART_TIME", "CONTRACT", "TEMPORARY", "OTHER", "VOLUNTEER", "INTERNSHIP"
|
|
992
|
+
] = "FULL_TIME",
|
|
993
|
+
auto_rejection_template: str | None = None,
|
|
994
|
+
screening_questions: list[dict[str, Any]] | None = None,
|
|
995
|
+
recruiter: dict[str, Any] | None = None,
|
|
996
|
+
) -> dict[str, Any]:
|
|
997
|
+
"""
|
|
998
|
+
Create a new job offer draft.
|
|
999
|
+
|
|
1000
|
+
Args:
|
|
1001
|
+
job_title: Required. A dictionary containing either {"id": "..."} or {"text": "..."}.
|
|
1002
|
+
company: Required. A dictionary containing either {"id": "..."} or {"text": "..."}.
|
|
1003
|
+
workplace: Required. One of "ON_SITE", "HYBRID", "REMOTE".
|
|
1004
|
+
location: Required. The ID of the location parameter. Use type LOCATION on the List search parameters route.
|
|
1005
|
+
description: Required. HTML description of the job.
|
|
1006
|
+
employment_status: Optional. One of "FULL_TIME", "PART_TIME", "CONTRACT", "TEMPORARY", "OTHER", "VOLUNTEER", "INTERNSHIP".
|
|
1007
|
+
auto_rejection_template: Optional. A rejection message template.
|
|
1008
|
+
screening_questions: Optional. A list of screening questions.
|
|
1009
|
+
recruiter: Optional. Recruiter object containing:
|
|
1010
|
+
- project: Required. {"id": "..."} or {"name": "..."}.
|
|
1011
|
+
- functions: Required. List of strings (job function IDs).
|
|
1012
|
+
- industries: Required. List of strings (industry IDs).
|
|
1013
|
+
- seniority: Required. Enum (e.g. "INTERNSHIP", "ENTRY_LEVEL", "ASSOCIATE", "MID_SENIOR_LEVEL", "DIRECTOR", "EXECUTIVE", "NOT_APPLICABLE").
|
|
1014
|
+
- apply_method: Required. {"apply_within_linkedin": ...} or {"apply_through_external_website": ...}.
|
|
1015
|
+
- include_poster_info: Optional boolean.
|
|
1016
|
+
- tracking_pixel_url: Optional string.
|
|
1017
|
+
- company_job_id: Optional string.
|
|
1018
|
+
- auto_archive_applicants: Optional object.
|
|
1019
|
+
- send_rejection_notification: Optional boolean.
|
|
1020
|
+
|
|
1021
|
+
Returns:
|
|
1022
|
+
A dictionary containing the response from the API.
|
|
1023
|
+
|
|
1024
|
+
Raises:
|
|
1025
|
+
httpx.HTTPError: If the API request fails.
|
|
1026
|
+
"""
|
|
1027
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs"
|
|
1028
|
+
payload: dict[str, Any] = {
|
|
1029
|
+
"account_id": await self._get_account_id(),
|
|
1030
|
+
"job_title": job_title,
|
|
1031
|
+
"company": company,
|
|
1032
|
+
"workplace": workplace,
|
|
1033
|
+
"location": location,
|
|
1034
|
+
"description": description,
|
|
1035
|
+
"employment_status": employment_status,
|
|
1036
|
+
}
|
|
1037
|
+
if auto_rejection_template:
|
|
1038
|
+
payload["auto_rejection_template"] = auto_rejection_template
|
|
1039
|
+
if screening_questions:
|
|
1040
|
+
payload["screening_questions"] = screening_questions
|
|
1041
|
+
if recruiter:
|
|
1042
|
+
payload["recruiter"] = recruiter
|
|
1043
|
+
|
|
1044
|
+
response = await self._apost(url, data=payload)
|
|
1045
|
+
return self._handle_response(response)
|
|
1046
|
+
|
|
1047
|
+
async def close_job_posting(
|
|
1048
|
+
self,
|
|
1049
|
+
job_id: str,
|
|
1050
|
+
service: Literal["CLASSIC", "RECRUITER"] | None = None,
|
|
1051
|
+
) -> dict[str, Any]:
|
|
1052
|
+
"""
|
|
1053
|
+
Close a job offer you have posted.
|
|
1054
|
+
|
|
1055
|
+
Args:
|
|
1056
|
+
job_id: Required. The ID of the job offer.
|
|
1057
|
+
service: Optional. The Linkedin service the job posting depends on.
|
|
1058
|
+
|
|
1059
|
+
Returns:
|
|
1060
|
+
A dictionary containing the response from the API.
|
|
1061
|
+
|
|
1062
|
+
Raises:
|
|
1063
|
+
httpx.HTTPError: If the API request fails.
|
|
1064
|
+
"""
|
|
1065
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/{job_id}/close"
|
|
1066
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
1067
|
+
if service:
|
|
1068
|
+
params["service"] = service
|
|
1069
|
+
response = await self._apost(url, params=params, data={})
|
|
1070
|
+
return self._handle_response(response)
|
|
1071
|
+
|
|
1072
|
+
async def retrieve_job_posting(
|
|
1073
|
+
self,
|
|
1074
|
+
job_id: str,
|
|
1075
|
+
service: Literal["CLASSIC", "RECRUITER"] = "CLASSIC",
|
|
1076
|
+
) -> dict[str, Any]:
|
|
1077
|
+
"""
|
|
1078
|
+
Retrieve a job offer.
|
|
1079
|
+
|
|
1080
|
+
Args:
|
|
1081
|
+
job_id: Required. The ID of the job offer.
|
|
1082
|
+
service: Required. The Linkedin service the job posting depends on. Default is CLASSIC.
|
|
1083
|
+
|
|
1084
|
+
Returns:
|
|
1085
|
+
A dictionary containing the job offer details.
|
|
1086
|
+
|
|
1087
|
+
Raises:
|
|
1088
|
+
httpx.HTTPError: If the API request fails.
|
|
1089
|
+
"""
|
|
1090
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/{job_id}"
|
|
1091
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id(), "service": service}
|
|
1092
|
+
response = await self._aget(url, params=params)
|
|
1093
|
+
return self._handle_response(response)
|
|
1094
|
+
|
|
1095
|
+
async def publish_job_posting(
|
|
1096
|
+
self,
|
|
1097
|
+
draft_id: str,
|
|
1098
|
+
mode: Literal["FREE"] = "FREE",
|
|
1099
|
+
service: Literal["CLASSIC", "RECRUITER"] = "CLASSIC",
|
|
1100
|
+
hiring_photo_frame: bool | None = None,
|
|
1101
|
+
bypass_email_verification: bool | None = None,
|
|
1102
|
+
) -> dict[str, Any]:
|
|
1103
|
+
"""
|
|
1104
|
+
Publish the job posting draft you have been working on.
|
|
1105
|
+
|
|
1106
|
+
Args:
|
|
1107
|
+
draft_id: Required. The id of the draft to publish.
|
|
1108
|
+
mode: Required. "FREE".
|
|
1109
|
+
service: Optional. The Linkedin service the job posting depends on. Default is CLASSIC.
|
|
1110
|
+
hiring_photo_frame: Optional. Whether or not to add the hiring photo frame to you profile picture.
|
|
1111
|
+
bypass_email_verification: Optional. Whether or not to verify if you're allowed to post a job on behalf on the current company.
|
|
1112
|
+
|
|
1113
|
+
Returns:
|
|
1114
|
+
A dictionary containing the response from the API.
|
|
1115
|
+
|
|
1116
|
+
Raises:
|
|
1117
|
+
httpx.HTTPError: If the API request fails.
|
|
1118
|
+
"""
|
|
1119
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/{draft_id}/publish"
|
|
1120
|
+
payload: dict[str, Any] = {
|
|
1121
|
+
"account_id": await self._get_account_id(),
|
|
1122
|
+
"mode": mode,
|
|
1123
|
+
"service": service,
|
|
1124
|
+
}
|
|
1125
|
+
if hiring_photo_frame is not None:
|
|
1126
|
+
payload["hiring_photo_frame"] = hiring_photo_frame
|
|
1127
|
+
if bypass_email_verification is not None:
|
|
1128
|
+
payload["bypass_email_verification"] = bypass_email_verification
|
|
1129
|
+
|
|
1130
|
+
response = await self._apost(url, data=payload)
|
|
1131
|
+
return self._handle_response(response)
|
|
1132
|
+
|
|
1133
|
+
async def solve_job_publishing_checkpoint(
|
|
1134
|
+
self,
|
|
1135
|
+
draft_id: str,
|
|
1136
|
+
input: str,
|
|
1137
|
+
) -> dict[str, Any]:
|
|
1138
|
+
"""
|
|
1139
|
+
Solve a checkpoint to verify your member privileges.
|
|
1140
|
+
|
|
1141
|
+
Args:
|
|
1142
|
+
draft_id: Required. The id of the draft to solve the checkpoint from.
|
|
1143
|
+
input: Required. The code or input to solve the checkpoint.
|
|
1144
|
+
|
|
1145
|
+
Returns:
|
|
1146
|
+
A dictionary containing the response from the API.
|
|
1147
|
+
|
|
1148
|
+
Raises:
|
|
1149
|
+
httpx.HTTPError: If the API request fails.
|
|
1150
|
+
"""
|
|
1151
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/{draft_id}/checkpoint"
|
|
1152
|
+
payload: dict[str, Any] = {
|
|
1153
|
+
"account_id": await self._get_account_id(),
|
|
1154
|
+
"input": input,
|
|
1155
|
+
}
|
|
1156
|
+
response = await self._apost(url, data=payload)
|
|
1157
|
+
return self._handle_response(response)
|
|
1158
|
+
|
|
1159
|
+
async def list_job_applicants(
|
|
1160
|
+
self,
|
|
1161
|
+
job_id: str,
|
|
1162
|
+
limit: int = 100,
|
|
1163
|
+
cursor: str | None = None,
|
|
1164
|
+
service: Literal["CLASSIC", "RECRUITER"] = "CLASSIC",
|
|
1165
|
+
sort_by: Literal[
|
|
1166
|
+
"relevance", "alphabetical", "newest_first", "screening_requirements"
|
|
1167
|
+
]
|
|
1168
|
+
| None = None,
|
|
1169
|
+
keywords: str | None = None,
|
|
1170
|
+
ratings: str | None = None,
|
|
1171
|
+
min_years_in_company: float | None = None,
|
|
1172
|
+
max_years_in_company: float | None = None,
|
|
1173
|
+
min_years_in_position: float | None = None,
|
|
1174
|
+
max_years_in_position: float | None = None,
|
|
1175
|
+
min_years_of_experience: float | None = None,
|
|
1176
|
+
max_years_of_experience: float | None = None,
|
|
1177
|
+
) -> dict[str, Any]:
|
|
1178
|
+
"""
|
|
1179
|
+
Retrieve all the users that have applied to a given offer.
|
|
1180
|
+
|
|
1181
|
+
Args:
|
|
1182
|
+
job_id: Required. The ID of the job offer.
|
|
1183
|
+
limit: Optional. The number of results to return. Default 100.
|
|
1184
|
+
cursor: Optional. The cursor to retrieve the next page.
|
|
1185
|
+
service: Optional. The Linkedin service the job posting depends on. Default is CLASSIC.
|
|
1186
|
+
sort_by: Optional. The sorting rule for applicants. Recruiter only.
|
|
1187
|
+
keywords: Optional. Filter results with keywords.
|
|
1188
|
+
ratings: Optional. One or more ratings (UNRATED, GOOD_FIT, MAYBE, NOT_A_FIT) separated by commas.
|
|
1189
|
+
min_years_in_company: Optional. Linkedin Recruiter native filter.
|
|
1190
|
+
max_years_in_company: Optional. Linkedin Recruiter native filter.
|
|
1191
|
+
min_years_in_position: Optional. Linkedin Recruiter native filter.
|
|
1192
|
+
max_years_in_position: Optional. Linkedin Recruiter native filter.
|
|
1193
|
+
min_years_of_experience: Optional. Linkedin Recruiter native filter.
|
|
1194
|
+
max_years_of_experience: Optional. Linkedin Recruiter native filter.
|
|
1195
|
+
|
|
1196
|
+
Returns:
|
|
1197
|
+
A dictionary containing the list of applicants.
|
|
815
1198
|
|
|
816
|
-
|
|
1199
|
+
Raises:
|
|
1200
|
+
httpx.HTTPError: If the API request fails.
|
|
1201
|
+
"""
|
|
1202
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/{job_id}/applicants"
|
|
1203
|
+
params: dict[str, Any] = {
|
|
1204
|
+
"account_id": await self._get_account_id(),
|
|
1205
|
+
"limit": limit,
|
|
1206
|
+
"service": service,
|
|
1207
|
+
}
|
|
1208
|
+
if cursor:
|
|
1209
|
+
params["cursor"] = cursor
|
|
1210
|
+
if sort_by:
|
|
1211
|
+
params["sort_by"] = sort_by
|
|
1212
|
+
if keywords:
|
|
1213
|
+
params["keywords"] = keywords
|
|
1214
|
+
if ratings:
|
|
1215
|
+
params["ratings"] = ratings
|
|
1216
|
+
if min_years_in_company is not None:
|
|
1217
|
+
params["min_years_in_company"] = min_years_in_company
|
|
1218
|
+
if max_years_in_company is not None:
|
|
1219
|
+
params["max_years_in_company"] = max_years_in_company
|
|
1220
|
+
if min_years_in_position is not None:
|
|
1221
|
+
params["min_years_in_position"] = min_years_in_position
|
|
1222
|
+
if max_years_in_position is not None:
|
|
1223
|
+
params["max_years_in_position"] = max_years_in_position
|
|
1224
|
+
if min_years_of_experience is not None:
|
|
1225
|
+
params["min_years_of_experience"] = min_years_of_experience
|
|
1226
|
+
if max_years_of_experience is not None:
|
|
1227
|
+
params["max_years_of_experience"] = max_years_of_experience
|
|
1228
|
+
|
|
1229
|
+
response = await self._aget(url, params=params)
|
|
1230
|
+
return self._handle_response(response)
|
|
1231
|
+
|
|
1232
|
+
async def retrieve_job_applicant(
|
|
1233
|
+
self,
|
|
1234
|
+
applicant_id: str,
|
|
1235
|
+
) -> dict[str, Any]:
|
|
1236
|
+
"""
|
|
1237
|
+
Retrieve the details of a user that has applied to a given offer. Applies to Classic job posting only.
|
|
1238
|
+
|
|
1239
|
+
Args:
|
|
1240
|
+
applicant_id: Required. The ID of the applicant.
|
|
1241
|
+
|
|
1242
|
+
Returns:
|
|
1243
|
+
A dictionary containing the applicant details.
|
|
1244
|
+
|
|
1245
|
+
Raises:
|
|
1246
|
+
httpx.HTTPError: If the API request fails.
|
|
1247
|
+
"""
|
|
1248
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/applicants/{applicant_id}"
|
|
1249
|
+
params: dict[str, Any] = {"account_id": await self._get_account_id()}
|
|
1250
|
+
response = await self._aget(url, params=params)
|
|
1251
|
+
return self._handle_response(response)
|
|
1252
|
+
|
|
1253
|
+
async def download_job_applicant_resume(
|
|
1254
|
+
self,
|
|
1255
|
+
applicant_id: str,
|
|
1256
|
+
service: Literal["CLASSIC", "RECRUITER"] = "CLASSIC",
|
|
1257
|
+
) -> dict[str, Any]:
|
|
1258
|
+
"""
|
|
1259
|
+
Download the resume of a job applicant.
|
|
1260
|
+
|
|
1261
|
+
Args:
|
|
1262
|
+
applicant_id: Required. The ID of the job applicant.
|
|
1263
|
+
service: Optional. The Linkedin service the applicant depends on. Default is classic.
|
|
1264
|
+
|
|
1265
|
+
Returns:
|
|
1266
|
+
A dictionary containing the resume details (likely a download URL or binary content, depending on API response).
|
|
1267
|
+
|
|
1268
|
+
Raises:
|
|
1269
|
+
httpx.HTTPError: If the API request fails.
|
|
1270
|
+
"""
|
|
1271
|
+
url = f"{self.base_url}/api/v1/linkedin/jobs/applicants/{applicant_id}/resume"
|
|
1272
|
+
params: dict[str, Any] = {
|
|
1273
|
+
"account_id": await self._get_account_id(),
|
|
1274
|
+
"service": service,
|
|
1275
|
+
}
|
|
1276
|
+
response = await self._aget(url, params=params)
|
|
817
1277
|
return self._handle_response(response)
|
|
818
1278
|
|
|
819
1279
|
def list_tools(self) -> list[Callable]:
|
|
820
1280
|
return [
|
|
1281
|
+
self.start_new_chat,
|
|
821
1282
|
self.list_all_chats,
|
|
822
1283
|
self.list_chat_messages,
|
|
823
1284
|
self.send_chat_message,
|
|
824
1285
|
self.retrieve_chat,
|
|
825
1286
|
self.list_all_messages,
|
|
826
1287
|
self.list_profile_posts,
|
|
1288
|
+
self.list_profile_comments,
|
|
827
1289
|
self.retrieve_own_profile,
|
|
828
1290
|
self.retrieve_user_profile,
|
|
829
1291
|
self.retrieve_post,
|
|
@@ -836,4 +1298,20 @@ class LinkedinApp(APIApplication):
|
|
|
836
1298
|
self.search_jobs,
|
|
837
1299
|
self.search_people,
|
|
838
1300
|
self.search_posts,
|
|
1301
|
+
self.send_invitation,
|
|
1302
|
+
self.list_sent_invitations,
|
|
1303
|
+
self.cancel_sent_invitation,
|
|
1304
|
+
self.list_received_invitations,
|
|
1305
|
+
self.handle_received_invitation,
|
|
1306
|
+
self.list_followers,
|
|
1307
|
+
# self.list_following this endpoint is not yet implemented by unipile
|
|
1308
|
+
self.list_job_postings,
|
|
1309
|
+
self.create_job_posting,
|
|
1310
|
+
self.close_job_posting,
|
|
1311
|
+
self.retrieve_job_posting,
|
|
1312
|
+
self.publish_job_posting,
|
|
1313
|
+
self.solve_job_publishing_checkpoint,
|
|
1314
|
+
self.list_job_applicants,
|
|
1315
|
+
self.retrieve_job_applicant,
|
|
1316
|
+
self.download_job_applicant_resume,
|
|
839
1317
|
]
|