universal-mcp-applications 0.1.1__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.
Files changed (268) hide show
  1. universal_mcp/applications/ahrefs/README.md +51 -0
  2. universal_mcp/applications/ahrefs/__init__.py +1 -0
  3. universal_mcp/applications/ahrefs/app.py +2291 -0
  4. universal_mcp/applications/airtable/README.md +22 -0
  5. universal_mcp/applications/airtable/__init__.py +1 -0
  6. universal_mcp/applications/airtable/app.py +479 -0
  7. universal_mcp/applications/apollo/README.md +44 -0
  8. universal_mcp/applications/apollo/__init__.py +1 -0
  9. universal_mcp/applications/apollo/app.py +1847 -0
  10. universal_mcp/applications/asana/README.md +199 -0
  11. universal_mcp/applications/asana/__init__.py +1 -0
  12. universal_mcp/applications/asana/app.py +9509 -0
  13. universal_mcp/applications/aws-s3/README.md +0 -0
  14. universal_mcp/applications/aws-s3/__init__.py +1 -0
  15. universal_mcp/applications/aws-s3/app.py +552 -0
  16. universal_mcp/applications/bill/README.md +0 -0
  17. universal_mcp/applications/bill/__init__.py +1 -0
  18. universal_mcp/applications/bill/app.py +8705 -0
  19. universal_mcp/applications/box/README.md +307 -0
  20. universal_mcp/applications/box/__init__.py +1 -0
  21. universal_mcp/applications/box/app.py +15987 -0
  22. universal_mcp/applications/braze/README.md +106 -0
  23. universal_mcp/applications/braze/__init__.py +1 -0
  24. universal_mcp/applications/braze/app.py +4754 -0
  25. universal_mcp/applications/cal-com-v2/README.md +150 -0
  26. universal_mcp/applications/cal-com-v2/__init__.py +1 -0
  27. universal_mcp/applications/cal-com-v2/app.py +5541 -0
  28. universal_mcp/applications/calendly/README.md +53 -0
  29. universal_mcp/applications/calendly/__init__.py +1 -0
  30. universal_mcp/applications/calendly/app.py +1436 -0
  31. universal_mcp/applications/canva/README.md +43 -0
  32. universal_mcp/applications/canva/__init__.py +1 -0
  33. universal_mcp/applications/canva/app.py +941 -0
  34. universal_mcp/applications/clickup/README.md +135 -0
  35. universal_mcp/applications/clickup/__init__.py +1 -0
  36. universal_mcp/applications/clickup/app.py +5009 -0
  37. universal_mcp/applications/coda/README.md +108 -0
  38. universal_mcp/applications/coda/__init__.py +1 -0
  39. universal_mcp/applications/coda/app.py +3671 -0
  40. universal_mcp/applications/confluence/README.md +198 -0
  41. universal_mcp/applications/confluence/__init__.py +1 -0
  42. universal_mcp/applications/confluence/app.py +6273 -0
  43. universal_mcp/applications/contentful/README.md +17 -0
  44. universal_mcp/applications/contentful/__init__.py +1 -0
  45. universal_mcp/applications/contentful/app.py +364 -0
  46. universal_mcp/applications/crustdata/README.md +25 -0
  47. universal_mcp/applications/crustdata/__init__.py +1 -0
  48. universal_mcp/applications/crustdata/app.py +586 -0
  49. universal_mcp/applications/dialpad/README.md +202 -0
  50. universal_mcp/applications/dialpad/__init__.py +1 -0
  51. universal_mcp/applications/dialpad/app.py +5949 -0
  52. universal_mcp/applications/digitalocean/README.md +463 -0
  53. universal_mcp/applications/digitalocean/__init__.py +1 -0
  54. universal_mcp/applications/digitalocean/app.py +20835 -0
  55. universal_mcp/applications/domain-checker/README.md +13 -0
  56. universal_mcp/applications/domain-checker/__init__.py +1 -0
  57. universal_mcp/applications/domain-checker/app.py +265 -0
  58. universal_mcp/applications/e2b/README.md +12 -0
  59. universal_mcp/applications/e2b/__init__.py +1 -0
  60. universal_mcp/applications/e2b/app.py +187 -0
  61. universal_mcp/applications/elevenlabs/README.md +88 -0
  62. universal_mcp/applications/elevenlabs/__init__.py +1 -0
  63. universal_mcp/applications/elevenlabs/app.py +3235 -0
  64. universal_mcp/applications/exa/README.md +15 -0
  65. universal_mcp/applications/exa/__init__.py +1 -0
  66. universal_mcp/applications/exa/app.py +221 -0
  67. universal_mcp/applications/falai/README.md +17 -0
  68. universal_mcp/applications/falai/__init__.py +1 -0
  69. universal_mcp/applications/falai/app.py +331 -0
  70. universal_mcp/applications/figma/README.md +49 -0
  71. universal_mcp/applications/figma/__init__.py +1 -0
  72. universal_mcp/applications/figma/app.py +1090 -0
  73. universal_mcp/applications/firecrawl/README.md +20 -0
  74. universal_mcp/applications/firecrawl/__init__.py +1 -0
  75. universal_mcp/applications/firecrawl/app.py +514 -0
  76. universal_mcp/applications/fireflies/README.md +25 -0
  77. universal_mcp/applications/fireflies/__init__.py +1 -0
  78. universal_mcp/applications/fireflies/app.py +506 -0
  79. universal_mcp/applications/fpl/README.md +23 -0
  80. universal_mcp/applications/fpl/__init__.py +1 -0
  81. universal_mcp/applications/fpl/app.py +1327 -0
  82. universal_mcp/applications/fpl/utils/api.py +142 -0
  83. universal_mcp/applications/fpl/utils/fixtures.py +629 -0
  84. universal_mcp/applications/fpl/utils/helper.py +982 -0
  85. universal_mcp/applications/fpl/utils/league_utils.py +546 -0
  86. universal_mcp/applications/fpl/utils/position_utils.py +68 -0
  87. universal_mcp/applications/ghost-content/README.md +25 -0
  88. universal_mcp/applications/ghost-content/__init__.py +1 -0
  89. universal_mcp/applications/ghost-content/app.py +654 -0
  90. universal_mcp/applications/github/README.md +1049 -0
  91. universal_mcp/applications/github/__init__.py +1 -0
  92. universal_mcp/applications/github/app.py +50600 -0
  93. universal_mcp/applications/gong/README.md +63 -0
  94. universal_mcp/applications/gong/__init__.py +1 -0
  95. universal_mcp/applications/gong/app.py +2297 -0
  96. universal_mcp/applications/google-ads/README.md +0 -0
  97. universal_mcp/applications/google-ads/__init__.py +1 -0
  98. universal_mcp/applications/google-ads/app.py +23 -0
  99. universal_mcp/applications/google-calendar/README.md +21 -0
  100. universal_mcp/applications/google-calendar/__init__.py +1 -0
  101. universal_mcp/applications/google-calendar/app.py +574 -0
  102. universal_mcp/applications/google-docs/README.md +25 -0
  103. universal_mcp/applications/google-docs/__init__.py +1 -0
  104. universal_mcp/applications/google-docs/app.py +760 -0
  105. universal_mcp/applications/google-drive/README.md +68 -0
  106. universal_mcp/applications/google-drive/__init__.py +1 -0
  107. universal_mcp/applications/google-drive/app.py +4936 -0
  108. universal_mcp/applications/google-gemini/README.md +25 -0
  109. universal_mcp/applications/google-gemini/__init__.py +1 -0
  110. universal_mcp/applications/google-gemini/app.py +663 -0
  111. universal_mcp/applications/google-mail/README.md +31 -0
  112. universal_mcp/applications/google-mail/__init__.py +1 -0
  113. universal_mcp/applications/google-mail/app.py +1354 -0
  114. universal_mcp/applications/google-searchconsole/README.md +21 -0
  115. universal_mcp/applications/google-searchconsole/__init__.py +1 -0
  116. universal_mcp/applications/google-searchconsole/app.py +320 -0
  117. universal_mcp/applications/google-sheet/README.md +36 -0
  118. universal_mcp/applications/google-sheet/__init__.py +1 -0
  119. universal_mcp/applications/google-sheet/app.py +1941 -0
  120. universal_mcp/applications/hashnode/README.md +20 -0
  121. universal_mcp/applications/hashnode/__init__.py +1 -0
  122. universal_mcp/applications/hashnode/app.py +455 -0
  123. universal_mcp/applications/heygen/README.md +44 -0
  124. universal_mcp/applications/heygen/__init__.py +1 -0
  125. universal_mcp/applications/heygen/app.py +961 -0
  126. universal_mcp/applications/http-tools/README.md +16 -0
  127. universal_mcp/applications/http-tools/__init__.py +1 -0
  128. universal_mcp/applications/http-tools/app.py +153 -0
  129. universal_mcp/applications/hubspot/README.md +239 -0
  130. universal_mcp/applications/hubspot/__init__.py +1 -0
  131. universal_mcp/applications/hubspot/app.py +416 -0
  132. universal_mcp/applications/jira/README.md +600 -0
  133. universal_mcp/applications/jira/__init__.py +1 -0
  134. universal_mcp/applications/jira/app.py +28804 -0
  135. universal_mcp/applications/klaviyo/README.md +313 -0
  136. universal_mcp/applications/klaviyo/__init__.py +1 -0
  137. universal_mcp/applications/klaviyo/app.py +11236 -0
  138. universal_mcp/applications/linkedin/README.md +15 -0
  139. universal_mcp/applications/linkedin/__init__.py +1 -0
  140. universal_mcp/applications/linkedin/app.py +243 -0
  141. universal_mcp/applications/mailchimp/README.md +281 -0
  142. universal_mcp/applications/mailchimp/__init__.py +1 -0
  143. universal_mcp/applications/mailchimp/app.py +10937 -0
  144. universal_mcp/applications/markitdown/README.md +12 -0
  145. universal_mcp/applications/markitdown/__init__.py +1 -0
  146. universal_mcp/applications/markitdown/app.py +63 -0
  147. universal_mcp/applications/miro/README.md +151 -0
  148. universal_mcp/applications/miro/__init__.py +1 -0
  149. universal_mcp/applications/miro/app.py +5429 -0
  150. universal_mcp/applications/ms-teams/README.md +42 -0
  151. universal_mcp/applications/ms-teams/__init__.py +1 -0
  152. universal_mcp/applications/ms-teams/app.py +1823 -0
  153. universal_mcp/applications/neon/README.md +74 -0
  154. universal_mcp/applications/neon/__init__.py +1 -0
  155. universal_mcp/applications/neon/app.py +2018 -0
  156. universal_mcp/applications/notion/README.md +30 -0
  157. universal_mcp/applications/notion/__init__.py +1 -0
  158. universal_mcp/applications/notion/app.py +527 -0
  159. universal_mcp/applications/openai/README.md +22 -0
  160. universal_mcp/applications/openai/__init__.py +1 -0
  161. universal_mcp/applications/openai/app.py +759 -0
  162. universal_mcp/applications/outlook/README.md +20 -0
  163. universal_mcp/applications/outlook/__init__.py +1 -0
  164. universal_mcp/applications/outlook/app.py +444 -0
  165. universal_mcp/applications/perplexity/README.md +12 -0
  166. universal_mcp/applications/perplexity/__init__.py +1 -0
  167. universal_mcp/applications/perplexity/app.py +65 -0
  168. universal_mcp/applications/pipedrive/README.md +284 -0
  169. universal_mcp/applications/pipedrive/__init__.py +1 -0
  170. universal_mcp/applications/pipedrive/app.py +12924 -0
  171. universal_mcp/applications/posthog/README.md +132 -0
  172. universal_mcp/applications/posthog/__init__.py +1 -0
  173. universal_mcp/applications/posthog/app.py +7125 -0
  174. universal_mcp/applications/reddit/README.md +135 -0
  175. universal_mcp/applications/reddit/__init__.py +1 -0
  176. universal_mcp/applications/reddit/app.py +4652 -0
  177. universal_mcp/applications/replicate/README.md +18 -0
  178. universal_mcp/applications/replicate/__init__.py +1 -0
  179. universal_mcp/applications/replicate/app.py +495 -0
  180. universal_mcp/applications/resend/README.md +40 -0
  181. universal_mcp/applications/resend/__init__.py +1 -0
  182. universal_mcp/applications/resend/app.py +881 -0
  183. universal_mcp/applications/retell/README.md +21 -0
  184. universal_mcp/applications/retell/__init__.py +1 -0
  185. universal_mcp/applications/retell/app.py +333 -0
  186. universal_mcp/applications/rocketlane/README.md +70 -0
  187. universal_mcp/applications/rocketlane/__init__.py +1 -0
  188. universal_mcp/applications/rocketlane/app.py +4346 -0
  189. universal_mcp/applications/semanticscholar/README.md +25 -0
  190. universal_mcp/applications/semanticscholar/__init__.py +1 -0
  191. universal_mcp/applications/semanticscholar/app.py +482 -0
  192. universal_mcp/applications/semrush/README.md +44 -0
  193. universal_mcp/applications/semrush/__init__.py +1 -0
  194. universal_mcp/applications/semrush/app.py +2081 -0
  195. universal_mcp/applications/sendgrid/README.md +362 -0
  196. universal_mcp/applications/sendgrid/__init__.py +1 -0
  197. universal_mcp/applications/sendgrid/app.py +9752 -0
  198. universal_mcp/applications/sentry/README.md +186 -0
  199. universal_mcp/applications/sentry/__init__.py +1 -0
  200. universal_mcp/applications/sentry/app.py +7471 -0
  201. universal_mcp/applications/serpapi/README.md +14 -0
  202. universal_mcp/applications/serpapi/__init__.py +1 -0
  203. universal_mcp/applications/serpapi/app.py +293 -0
  204. universal_mcp/applications/sharepoint/README.md +0 -0
  205. universal_mcp/applications/sharepoint/__init__.py +1 -0
  206. universal_mcp/applications/sharepoint/app.py +215 -0
  207. universal_mcp/applications/shopify/README.md +321 -0
  208. universal_mcp/applications/shopify/__init__.py +1 -0
  209. universal_mcp/applications/shopify/app.py +15392 -0
  210. universal_mcp/applications/shortcut/README.md +128 -0
  211. universal_mcp/applications/shortcut/__init__.py +1 -0
  212. universal_mcp/applications/shortcut/app.py +4478 -0
  213. universal_mcp/applications/slack/README.md +0 -0
  214. universal_mcp/applications/slack/__init__.py +1 -0
  215. universal_mcp/applications/slack/app.py +570 -0
  216. universal_mcp/applications/spotify/README.md +91 -0
  217. universal_mcp/applications/spotify/__init__.py +1 -0
  218. universal_mcp/applications/spotify/app.py +2526 -0
  219. universal_mcp/applications/supabase/README.md +87 -0
  220. universal_mcp/applications/supabase/__init__.py +1 -0
  221. universal_mcp/applications/supabase/app.py +2970 -0
  222. universal_mcp/applications/tavily/README.md +12 -0
  223. universal_mcp/applications/tavily/__init__.py +1 -0
  224. universal_mcp/applications/tavily/app.py +51 -0
  225. universal_mcp/applications/trello/README.md +266 -0
  226. universal_mcp/applications/trello/__init__.py +1 -0
  227. universal_mcp/applications/trello/app.py +10875 -0
  228. universal_mcp/applications/twillo/README.md +0 -0
  229. universal_mcp/applications/twillo/__init__.py +1 -0
  230. universal_mcp/applications/twillo/app.py +269 -0
  231. universal_mcp/applications/twitter/README.md +100 -0
  232. universal_mcp/applications/twitter/__init__.py +1 -0
  233. universal_mcp/applications/twitter/api_segments/__init__.py +0 -0
  234. universal_mcp/applications/twitter/api_segments/api_segment_base.py +51 -0
  235. universal_mcp/applications/twitter/api_segments/compliance_api.py +122 -0
  236. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +255 -0
  237. universal_mcp/applications/twitter/api_segments/dm_events_api.py +140 -0
  238. universal_mcp/applications/twitter/api_segments/likes_api.py +159 -0
  239. universal_mcp/applications/twitter/api_segments/lists_api.py +395 -0
  240. universal_mcp/applications/twitter/api_segments/openapi_json_api.py +34 -0
  241. universal_mcp/applications/twitter/api_segments/spaces_api.py +309 -0
  242. universal_mcp/applications/twitter/api_segments/trends_api.py +40 -0
  243. universal_mcp/applications/twitter/api_segments/tweets_api.py +1403 -0
  244. universal_mcp/applications/twitter/api_segments/usage_api.py +40 -0
  245. universal_mcp/applications/twitter/api_segments/users_api.py +1498 -0
  246. universal_mcp/applications/twitter/app.py +46 -0
  247. universal_mcp/applications/unipile/README.md +28 -0
  248. universal_mcp/applications/unipile/__init__.py +1 -0
  249. universal_mcp/applications/unipile/app.py +829 -0
  250. universal_mcp/applications/whatsapp/README.md +23 -0
  251. universal_mcp/applications/whatsapp/__init__.py +1 -0
  252. universal_mcp/applications/whatsapp/app.py +595 -0
  253. universal_mcp/applications/whatsapp-business/README.md +34 -0
  254. universal_mcp/applications/whatsapp-business/__init__.py +1 -0
  255. universal_mcp/applications/whatsapp-business/app.py +1065 -0
  256. universal_mcp/applications/wrike/README.md +46 -0
  257. universal_mcp/applications/wrike/__init__.py +1 -0
  258. universal_mcp/applications/wrike/app.py +1583 -0
  259. universal_mcp/applications/youtube/README.md +57 -0
  260. universal_mcp/applications/youtube/__init__.py +1 -0
  261. universal_mcp/applications/youtube/app.py +1696 -0
  262. universal_mcp/applications/zenquotes/README.md +12 -0
  263. universal_mcp/applications/zenquotes/__init__.py +1 -0
  264. universal_mcp/applications/zenquotes/app.py +31 -0
  265. universal_mcp_applications-0.1.1.dist-info/METADATA +172 -0
  266. universal_mcp_applications-0.1.1.dist-info/RECORD +268 -0
  267. universal_mcp_applications-0.1.1.dist-info/WHEEL +4 -0
  268. universal_mcp_applications-0.1.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1847 @@
1
+ from typing import Any
2
+
3
+ from loguru import logger
4
+ from universal_mcp.applications.application import APIApplication
5
+ from universal_mcp.integrations import Integration
6
+
7
+
8
+ class ApolloApp(APIApplication):
9
+ def __init__(self, integration: Integration = None, **kwargs) -> None:
10
+ super().__init__(name="apollo", integration=integration, **kwargs)
11
+ self.base_url = "https://api.apollo.io/api/v1"
12
+
13
+ def _get_headers(self) -> dict[str, str]:
14
+ """
15
+ Get the headers for Apollo API requests.
16
+ Overrides the base class method to use X-Api-Key.
17
+ """
18
+ if not self.integration:
19
+ logger.warning(
20
+ "ApolloApp: No integration configured, returning empty headers."
21
+ )
22
+ return {}
23
+
24
+ # ApiKeyIntegration's get_credentials() returns a dict like:
25
+ # {'api_key': 'your_actual_key_value'}
26
+ credentials = self.integration.get_credentials()
27
+
28
+ # The key in the credentials dict from ApiKeyIntegration is 'api_key'
29
+ api_key = (
30
+ credentials.get("api_key")
31
+ or credentials.get("API_KEY")
32
+ or credentials.get("apiKey")
33
+ )
34
+
35
+ if not api_key:
36
+ logger.error(
37
+ "ApolloApp: API key not found in integration credentials for Apollo."
38
+ )
39
+ # You might want to raise an error here if an API key is absolutely required
40
+ # For example: raise ValueError("API key is missing for Apollo integration.")
41
+ return { # Or return minimal headers if some calls might not need auth (unlikely for Apollo)
42
+ "Content-Type": "application/json",
43
+ "Cache-Control": "no-cache",
44
+ }
45
+
46
+ logger.debug("ApolloApp: Using X-Api-Key for authentication.")
47
+ return {
48
+ "X-Api-Key": api_key,
49
+ "Content-Type": "application/json",
50
+ "Cache-Control": "no-cache", # Often good practice for APIs
51
+ }
52
+
53
+ def people_enrichment(
54
+ self,
55
+ first_name: str | None = None,
56
+ last_name: str | None = None,
57
+ name: str | None = None,
58
+ email: str | None = None,
59
+ hashed_email: str | None = None,
60
+ organization_name: str | None = None,
61
+ domain: str | None = None,
62
+ id: str | None = None,
63
+ linkedin_url: str | None = None,
64
+ reveal_personal_emails: bool | None = None,
65
+ reveal_phone_number: bool | None = None,
66
+ webhook_url: str | None = None,
67
+ ) -> dict[str, Any]:
68
+ """
69
+ Matches a person based on provided identifying information such as name, email, organization, or LinkedIn URL, with options to reveal personal emails and phone numbers.
70
+
71
+ Args:
72
+ first_name (string): The first name of the person to match, typically used together with the last_name parameter to identify an individual; example: "Tim".
73
+ last_name (string): The last name of the person to match, usually combined with the `first_name` parameter to improve accuracy; for example, `zheng`.
74
+ name (string): Full name of the person, typically a first name and last name separated by a space, replacing `first_name` and `last_name`.
75
+ email (string): Email address of the person to match; must be valid (e.g., example@email.com).
76
+ hashed_email (string): The hashed_email query parameter accepts an email address hashed in MD5 or SHA-256 format, such as `8d935115b9ff4489f2d1f9249503cadf` (MD5) or `97817c0c49994eb500ad0a5e7e2d8aed51977b26424d508f66e4e8887746a152` (SHA-256).
77
+ organization_name (string): The name of the person's current or past employer, used to identify their organization during the matching process. Example: `apollo`.
78
+ domain (string): The domain parameter is the employer's website domain for the person being matched, reflecting either current or previous employment; enter only the domain name without prefixes like www or symbols such as @, for example, apollo.io or microsoft.com.
79
+ id (string): Unique ID for the person in Apollo's database, obtained via the People Search endpoint.
80
+ linkedin_url (string): The URL of the person's LinkedIn profile, typically starting with "https://www.linkedin.com/in/", used to uniquely identify and match the individual in the system.
81
+ reveal_personal_emails (boolean): Enrich person data with personal emails by setting to `true`. This may consume credits and is subject to GDPR restrictions.
82
+ reveal_phone_number (boolean): Set to `true` to enrich person's data with available phone numbers, consuming credits. Requires a `webhook_url` for asynchronous verification and delivery.
83
+ webhook_url (string): If `reveal_phone_number` is true, provide this required webhook URL where Apollo will send a JSON response containing the requested phone number; otherwise, omit this parameter.
84
+
85
+ Returns:
86
+ dict[str, Any]: 200
87
+
88
+ Raises:
89
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
90
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
91
+
92
+ Tags:
93
+ People, important
94
+ """
95
+ request_body_data = None
96
+ url = f"{self.base_url}/people/match"
97
+ query_params = {
98
+ k: v
99
+ for k, v in [
100
+ ("first_name", first_name),
101
+ ("last_name", last_name),
102
+ ("name", name),
103
+ ("email", email),
104
+ ("hashed_email", hashed_email),
105
+ ("organization_name", organization_name),
106
+ ("domain", domain),
107
+ ("id", id),
108
+ ("linkedin_url", linkedin_url),
109
+ ("reveal_personal_emails", reveal_personal_emails),
110
+ ("reveal_phone_number", reveal_phone_number),
111
+ ("webhook_url", webhook_url),
112
+ ]
113
+ if v is not None
114
+ }
115
+ response = self._post(
116
+ url,
117
+ data=request_body_data,
118
+ params=query_params,
119
+ content_type="application/json",
120
+ )
121
+ response.raise_for_status()
122
+ if (
123
+ response.status_code == 204
124
+ or not response.content
125
+ or not response.text.strip()
126
+ ):
127
+ return None
128
+ try:
129
+ return response.json()
130
+ except ValueError:
131
+ return None
132
+
133
+ def bulk_people_enrichment(
134
+ self,
135
+ reveal_personal_emails: bool | None = None,
136
+ reveal_phone_number: bool | None = None,
137
+ webhook_url: str | None = None,
138
+ details: list[dict[str, Any]] | None = None,
139
+ ) -> dict[str, Any]:
140
+ """
141
+ Performs a bulk match operation on people data using a POST request to the "/people/bulk_match" endpoint, accepting query parameters to control the reveal of personal emails and phone numbers, and optionally specifying a webhook URL, with the request body containing JSON data.
142
+
143
+ Args:
144
+ reveal_personal_emails (boolean): Set to true to enrich matched people with personal emails, consuming credits; defaults to false. Personal emails won’t be revealed for individuals in GDPR-compliant regions.
145
+ reveal_phone_number (boolean): Set to `true` to enrich matched people with available phone numbers, consuming credits. Requires a `webhook_url` for asynchronous verification and delivery of phone details.
146
+ webhook_url (string): Optional webhook URL for receiving a JSON response with phone numbers if `reveal_phone_number` is `true`.
147
+ details (array): Provide info for each person you want to enrich as an object within this array. Add up to 10 people.
148
+
149
+ Returns:
150
+ dict[str, Any]: 200
151
+
152
+ Raises:
153
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
154
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
155
+
156
+ Tags:
157
+ People
158
+ """
159
+ request_body_data = None
160
+ request_body_data = {
161
+ "details": details,
162
+ }
163
+ request_body_data = {
164
+ k: v for k, v in request_body_data.items() if v is not None
165
+ }
166
+ url = f"{self.base_url}/people/bulk_match"
167
+ query_params = {
168
+ k: v
169
+ for k, v in [
170
+ ("reveal_personal_emails", reveal_personal_emails),
171
+ ("reveal_phone_number", reveal_phone_number),
172
+ ("webhook_url", webhook_url),
173
+ ]
174
+ if v is not None
175
+ }
176
+ response = self._post(
177
+ url,
178
+ data=request_body_data,
179
+ params=query_params,
180
+ content_type="application/json",
181
+ )
182
+ response.raise_for_status()
183
+ if (
184
+ response.status_code == 204
185
+ or not response.content
186
+ or not response.text.strip()
187
+ ):
188
+ return None
189
+ try:
190
+ return response.json()
191
+ except ValueError:
192
+ return None
193
+
194
+ def organization_enrichment(self, domain: str) -> dict[str, Any]:
195
+ """
196
+ Retrieves enriched organization data for a company specified by its domain name.
197
+
198
+ Args:
199
+ domain (string): The domain query parameter specifies the company’s website domain to enrich, excluding prefixes like "www." or symbols such as "@"; for example, use "apollo.io" or "microsoft.com".
200
+
201
+ Returns:
202
+ dict[str, Any]: 200
203
+
204
+ Raises:
205
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
206
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
207
+
208
+ Tags:
209
+ Organizations
210
+ """
211
+ url = f"{self.base_url}/organizations/enrich"
212
+ query_params = {k: v for k, v in [("domain", domain)] if v is not None}
213
+ response = self._get(url, params=query_params)
214
+ response.raise_for_status()
215
+ if (
216
+ response.status_code == 204
217
+ or not response.content
218
+ or not response.text.strip()
219
+ ):
220
+ return None
221
+ try:
222
+ return response.json()
223
+ except ValueError:
224
+ return None
225
+
226
+ def bulk_organization_enrichment(self, domains_: list[str]) -> dict[str, Any]:
227
+ """
228
+ Enriches multiple organizations in bulk by submitting an array of domain names and returns detailed company profiles in a single request.
229
+
230
+ Args:
231
+ domains_ (array): List of company domains to enrich, without "www.", "@", or similar prefixes—just the base domain, e.g., apollo.io, microsoft.com.
232
+
233
+ Returns:
234
+ dict[str, Any]: 200
235
+
236
+ Raises:
237
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
238
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
239
+
240
+ Tags:
241
+ Organizations
242
+ """
243
+ request_body_data = None
244
+ url = f"{self.base_url}/organizations/bulk_enrich"
245
+ query_params = {k: v for k, v in [("domains[]", domains_)] if v is not None}
246
+ response = self._post(
247
+ url,
248
+ data=request_body_data,
249
+ params=query_params,
250
+ content_type="application/json",
251
+ )
252
+ response.raise_for_status()
253
+ if (
254
+ response.status_code == 204
255
+ or not response.content
256
+ or not response.text.strip()
257
+ ):
258
+ return None
259
+ try:
260
+ return response.json()
261
+ except ValueError:
262
+ return None
263
+
264
+ def people_search(
265
+ self,
266
+ person_titles_: list[str] | None = None,
267
+ include_similar_titles: bool | None = None,
268
+ person_locations_: list[str] | None = None,
269
+ person_seniorities_: list[str] | None = None,
270
+ organization_locations_: list[str] | None = None,
271
+ q_organization_domains_list_: list[str] | None = None,
272
+ contact_email_status_: list[str] | None = None,
273
+ organization_ids_: list[str] | None = None,
274
+ organization_num_employees_ranges_: list[str] | None = None,
275
+ q_keywords: str | None = None,
276
+ page: int | None = None,
277
+ per_page: int | None = None,
278
+ ) -> dict[str, Any]:
279
+ """
280
+ Searches for people matching specified criteria including titles, locations, seniorities, organization details, and keywords, returning paginated results.
281
+
282
+ Args:
283
+ person_titles_ (array): Specify job titles to find matching people; only one listed title needs to match, and partial matches are included. Combine with person_seniorities[] for refined results.
284
+ include_similar_titles (boolean): This parameter controls whether the search includes people with job titles similar to those specified in `person_titles[]`; set to false to return only exact title matches. Example: 'true'.
285
+ person_locations_ (array): The locations where people reside, searchable by city, US state, or country; to filter by employer headquarters, use the `organization_locations` parameter instead.
286
+ person_seniorities_ (array): The 'person_seniorities[]' query parameter filters people by their current job seniority level within their employer, such as Director or Senior IC; matching any listed seniority expands results, which reflect only present titles and can be combined
287
+ organization_locations_ (array): The organization_locations[] parameter filters people by the headquarters location of their current employer’s company, searchable by city, state, or country; office locations do not affect results.
288
+ q_organization_domains_list_ (array): The domain names of a person’s current or past employers, without prefixes like `www.` or symbols like `@`; accepts up to 1,000 domains per request, e.g., `apollo.io`, `microsoft.com`.
289
+ contact_email_status_ (array): The contact_email_status[] parameter filters people by their email verification status; you can specify multiple values like verified, unverified, likely to engage, or unavailable to broaden your search.
290
+ organization_ids_ (array): The Apollo IDs of companies (employers) to include in your search results; each company has a unique ID found via the Organization Search endpoint (e.g., `5e66b6381e05b4008c8331b8`).
291
+ organization_num_employees_ranges_ (array): Filter people by the employee count of their company using ranges (e.g., `1,10`, `250,500`). Multiple ranges can be added to expand search results.
292
+ q_keywords (string): A string of keywords used to filter search results.
293
+ page (integer): The page number of Apollo data to retrieve, used with `per_page` to paginate results and optimize endpoint performance; for example, `4`.
294
+ per_page (integer): The number of results to return per page in the search response; limiting this improves performance. Use the `page` parameter to access additional pages of results.
295
+
296
+ Returns:
297
+ dict[str, Any]: 200
298
+
299
+ Raises:
300
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
301
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
302
+
303
+ Tags:
304
+ People
305
+ """
306
+ request_body_data = None
307
+ url = f"{self.base_url}/mixed_people/search"
308
+ query_params = {
309
+ k: v
310
+ for k, v in [
311
+ ("person_titles[]", person_titles_),
312
+ ("include_similar_titles", include_similar_titles),
313
+ ("person_locations[]", person_locations_),
314
+ ("person_seniorities[]", person_seniorities_),
315
+ ("organization_locations[]", organization_locations_),
316
+ ("q_organization_domains_list[]", q_organization_domains_list_),
317
+ ("contact_email_status[]", contact_email_status_),
318
+ ("organization_ids[]", organization_ids_),
319
+ (
320
+ "organization_num_employees_ranges[]",
321
+ organization_num_employees_ranges_,
322
+ ),
323
+ ("q_keywords", q_keywords),
324
+ ("page", page),
325
+ ("per_page", per_page),
326
+ ]
327
+ if v is not None
328
+ }
329
+ response = self._post(
330
+ url,
331
+ data=request_body_data,
332
+ params=query_params,
333
+ content_type="application/json",
334
+ )
335
+ response.raise_for_status()
336
+ if (
337
+ response.status_code == 204
338
+ or not response.content
339
+ or not response.text.strip()
340
+ ):
341
+ return None
342
+ try:
343
+ return response.json()
344
+ except ValueError:
345
+ return None
346
+
347
+ def organization_search(
348
+ self,
349
+ organization_num_employees_ranges_: list[str] | None = None,
350
+ organization_locations_: list[str] | None = None,
351
+ organization_not_locations_: list[str] | None = None,
352
+ revenue_range_min: int | None = None,
353
+ revenue_range_max: int | None = None,
354
+ currently_using_any_of_technology_uids_: list[str] | None = None,
355
+ q_organization_keyword_tags_: list[str] | None = None,
356
+ q_organization_name: str | None = None,
357
+ organization_ids_: list[str] | None = None,
358
+ page: int | None = None,
359
+ per_page: int | None = None,
360
+ ) -> dict[str, Any]:
361
+ """
362
+ Searches mixed companies based on various criteria such as employee ranges, locations, revenue range, technology usage, keyword tags, organization names, and IDs, supporting pagination.
363
+
364
+ Args:
365
+ organization_num_employees_ranges_ (array): Specify company employee headcount ranges by listing strings like "1,10" or "250,500"; multiple ranges are allowed.
366
+ organization_locations_ (array): The organization_locations[] parameter filters companies by their headquarters' location—city, state, or country; multiple office locations are ignored, and to exclude locations, use organization_not_locations.
367
+ organization_not_locations_ (array): Exclude companies headquartered in specified locations such as cities, US states, or countries from search results to avoid prospecting in unwanted territories (e.g., `ireland`, `minnesota`, `seoul`).
368
+ revenue_range_min (integer): Sets the minimum revenue value for filtering organizations; enter only digits, without currency symbols, commas, or decimals.
369
+ revenue_range_max (integer): Set the maximum organization revenue for search filtering as an integer without currency symbols, commas, or decimals; pair with `revenue_range[min]` to define a revenue range (e.g., 50000000).
370
+ currently_using_any_of_technology_uids_ (array): Filter organizations by the technologies they currently use from over 1,500 options, identified with underscores replacing spaces and periods as in the provided technology list.
371
+ q_organization_keyword_tags_ (array): Filter search results by specifying keywords related to company industries, services, or focus areas, such as "mining," "sales strategy," or "consulting," to return matching companies.
372
+ q_organization_name (string): Filters results to companies whose names contain the specified value (partial match allowed); unmatched names are excluded, regardless of other criteria.
373
+ organization_ids_ (array): The Apollo unique company IDs to filter search results by specific organizations; each ID corresponds to a company in the Apollo database, e.g., `5e66b6381e05b4008c8331b8`.
374
+ page (integer): The page number of Apollo data to retrieve, used with `per_page` to paginate results and optimize performance; for example, `4` returns the fourth page of results.
375
+ per_page (integer): The number of search results returned per page to limit response size and improve performance; use the `page` parameter to access additional pages. Example: `10`.
376
+
377
+ Returns:
378
+ dict[str, Any]: 200
379
+
380
+ Raises:
381
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
382
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
383
+
384
+ Tags:
385
+ Organizations
386
+ """
387
+ request_body_data = None
388
+ url = f"{self.base_url}/mixed_companies/search"
389
+ query_params = {
390
+ k: v
391
+ for k, v in [
392
+ (
393
+ "organization_num_employees_ranges[]",
394
+ organization_num_employees_ranges_,
395
+ ),
396
+ ("organization_locations[]", organization_locations_),
397
+ ("organization_not_locations[]", organization_not_locations_),
398
+ ("revenue_range[min]", revenue_range_min),
399
+ ("revenue_range[max]", revenue_range_max),
400
+ (
401
+ "currently_using_any_of_technology_uids[]",
402
+ currently_using_any_of_technology_uids_,
403
+ ),
404
+ ("q_organization_keyword_tags[]", q_organization_keyword_tags_),
405
+ ("q_organization_name", q_organization_name),
406
+ ("organization_ids[]", organization_ids_),
407
+ ("page", page),
408
+ ("per_page", per_page),
409
+ ]
410
+ if v is not None
411
+ }
412
+ response = self._post(
413
+ url,
414
+ data=request_body_data,
415
+ params=query_params,
416
+ content_type="application/json",
417
+ )
418
+ response.raise_for_status()
419
+ if (
420
+ response.status_code == 204
421
+ or not response.content
422
+ or not response.text.strip()
423
+ ):
424
+ return None
425
+ try:
426
+ return response.json()
427
+ except ValueError:
428
+ return None
429
+
430
+ def organization_jobs_postings(
431
+ self,
432
+ organization_id: str,
433
+ page: int | None = None,
434
+ per_page: int | None = None,
435
+ ) -> dict[str, Any]:
436
+ """
437
+ Retrieves a paginated list of job postings for a specified organization using the "GET" method, allowing optional pagination parameters for page and items per page.
438
+
439
+ Args:
440
+ organization_id (string): organization_id
441
+ page (integer): The page query parameter specifies which page of job postings to retrieve, used with per_page to paginate results and improve response performance.
442
+ per_page (integer): The number of results to return per page in the response, which improves performance by limiting data size; use the `page` parameter to access other pages. Example: 10
443
+
444
+ Returns:
445
+ dict[str, Any]: 200
446
+
447
+ Raises:
448
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
449
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
450
+
451
+ Tags:
452
+ Organizations
453
+ """
454
+ if organization_id is None:
455
+ raise ValueError("Missing required parameter 'organization_id'.")
456
+ url = f"{self.base_url}/organizations/{organization_id}/job_postings"
457
+ query_params = {
458
+ k: v for k, v in [("page", page), ("per_page", per_page)] if v is not None
459
+ }
460
+ response = self._get(url, params=query_params)
461
+ response.raise_for_status()
462
+ if (
463
+ response.status_code == 204
464
+ or not response.content
465
+ or not response.text.strip()
466
+ ):
467
+ return None
468
+ try:
469
+ return response.json()
470
+ except ValueError:
471
+ return None
472
+
473
+ def create_an_account(
474
+ self,
475
+ name: str | None = None,
476
+ domain: str | None = None,
477
+ owner_id: str | None = None,
478
+ account_stage_id: str | None = None,
479
+ phone: str | None = None,
480
+ raw_address: str | None = None,
481
+ ) -> dict[str, Any]:
482
+ """
483
+ Creates a new account resource using the provided query parameters such as name, domain, owner ID, account stage ID, phone, and raw address.
484
+
485
+ Args:
486
+ name (string): A unique, human-readable name for the new account, such as "The Irish Copywriters."
487
+ domain (string): The domain name for the account, entered without any prefixes like "www."; for example, use "apollo.io" or "microsoft.com".
488
+ owner_id (string): The unique identifier of the account owner within your team's Apollo account, required to assign ownership; retrieve valid user IDs using the Get a List of Users endpoint.
489
+ account_stage_id (string): Apollo ID of the account stage to assign the account to; otherwise, it defaults to your team's settings.
490
+ phone (string): The primary phone number for the account, which may be for headquarters, a branch, or a main contact; any format is accepted and sanitized for consistency in the response.
491
+ raw_address (string): Corporate location for the account, including city, state, and country, matched to a pre-defined location by Apollo.
492
+
493
+ Returns:
494
+ dict[str, Any]: 200
495
+
496
+ Raises:
497
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
498
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
499
+
500
+ Tags:
501
+ Accounts
502
+ """
503
+ request_body_data = None
504
+ url = f"{self.base_url}/accounts"
505
+ query_params = {
506
+ k: v
507
+ for k, v in [
508
+ ("name", name),
509
+ ("domain", domain),
510
+ ("owner_id", owner_id),
511
+ ("account_stage_id", account_stage_id),
512
+ ("phone", phone),
513
+ ("raw_address", raw_address),
514
+ ]
515
+ if v is not None
516
+ }
517
+ response = self._post(
518
+ url,
519
+ data=request_body_data,
520
+ params=query_params,
521
+ content_type="application/json",
522
+ )
523
+ response.raise_for_status()
524
+ if (
525
+ response.status_code == 204
526
+ or not response.content
527
+ or not response.text.strip()
528
+ ):
529
+ return None
530
+ try:
531
+ return response.json()
532
+ except ValueError:
533
+ return None
534
+
535
+ def update_an_account(
536
+ self,
537
+ account_id: str,
538
+ name: str | None = None,
539
+ domain: str | None = None,
540
+ owner_id: str | None = None,
541
+ account_stage_id: str | None = None,
542
+ raw_address: str | None = None,
543
+ phone: str | None = None,
544
+ ) -> dict[str, Any]:
545
+ """
546
+ Updates an account identified by `{account_id}` with specified parameters such as `name`, `domain`, `owner_id`, `account_stage_id`, `raw_address`, and `phone`, returning a status message upon successful modification.
547
+
548
+ Args:
549
+ account_id (string): account_id
550
+ name (string): Specify the account's human-readable name. Example: "The Fast Irish Copywriters"
551
+ domain (string): Specify the account's domain name to update, excluding any prefixes like "www."; use only the base domain such as "example.com" or "company.org".
552
+ owner_id (string): The ID of the user within your Apollo team to assign as the account owner; changing this updates the account owner. Retrieve valid user IDs via the Get a List of Users endpoint.
553
+ account_stage_id (string): The Apollo ID of the account stage to assign or update the account's stage; omit to auto-assign based on your team's default settings. Use List Account Stages to find IDs.
554
+ raw_address (string): Update the corporate location for the account. Provide a city, state, and country to match our pre-defined locations. Examples: `Belfield, Dublin 4, Ireland`; `Dallas, United States`.
555
+ phone (string): Update the account's primary phone number, which may be the corporate headquarters, a branch, or a direct contact; input in any format, sanitized and returned formatted.
556
+
557
+ Returns:
558
+ dict[str, Any]: 200
559
+
560
+ Raises:
561
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
562
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
563
+
564
+ Tags:
565
+ Accounts
566
+ """
567
+ if account_id is None:
568
+ raise ValueError("Missing required parameter 'account_id'.")
569
+ request_body_data = None
570
+ url = f"{self.base_url}/accounts/{account_id}"
571
+ query_params = {
572
+ k: v
573
+ for k, v in [
574
+ ("name", name),
575
+ ("domain", domain),
576
+ ("owner_id", owner_id),
577
+ ("account_stage_id", account_stage_id),
578
+ ("raw_address", raw_address),
579
+ ("phone", phone),
580
+ ]
581
+ if v is not None
582
+ }
583
+ response = self._put(
584
+ url,
585
+ data=request_body_data,
586
+ params=query_params,
587
+ content_type="application/json",
588
+ )
589
+ response.raise_for_status()
590
+ if (
591
+ response.status_code == 204
592
+ or not response.content
593
+ or not response.text.strip()
594
+ ):
595
+ return None
596
+ try:
597
+ return response.json()
598
+ except ValueError:
599
+ return None
600
+
601
+ def search_for_accounts(
602
+ self,
603
+ q_organization_name: str | None = None,
604
+ account_stage_ids_: list[str] | None = None,
605
+ sort_by_field: str | None = None,
606
+ sort_ascending: bool | None = None,
607
+ page: int | None = None,
608
+ per_page: int | None = None,
609
+ ) -> dict[str, Any]:
610
+ """
611
+ Searches for accounts based on organization name, account stage IDs, sorting, and pagination parameters, returning matching results.
612
+
613
+ Args:
614
+ q_organization_name (string): Search for accounts by name using keywords. Matches part of the name, e.g., 'marketing' returns 'NY Marketing Unlimited'.
615
+ account_stage_ids_ (array): Specify Apollo IDs for account stages to include in search results. Multiple stages will match any of them.
616
+ sort_by_field (string): Sort matching accounts by one of these fields: `account_last_activity_date` (most recent activity first), `account_created_at` (newest accounts first), or `account_updated_at` (most recently updated first).
617
+ sort_ascending (boolean): Sort results in ascending order using the specified `sort_by_field`.
618
+ page (integer): The page number of the Apollo data to retrieve, used with the `per_page` parameter to paginate search results and optimize endpoint performance; for example, `4`.
619
+ per_page (integer): The number of search results returned per page to improve response performance; use the `page` parameter to access additional pages. Example value: `10`.
620
+
621
+ Returns:
622
+ dict[str, Any]: 200
623
+
624
+ Raises:
625
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
626
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
627
+
628
+ Tags:
629
+ Accounts
630
+ """
631
+ request_body_data = None
632
+ url = f"{self.base_url}/accounts/search"
633
+ query_params = {
634
+ k: v
635
+ for k, v in [
636
+ ("q_organization_name", q_organization_name),
637
+ ("account_stage_ids[]", account_stage_ids_),
638
+ ("sort_by_field", sort_by_field),
639
+ ("sort_ascending", sort_ascending),
640
+ ("page", page),
641
+ ("per_page", per_page),
642
+ ]
643
+ if v is not None
644
+ }
645
+ response = self._post(
646
+ url,
647
+ data=request_body_data,
648
+ params=query_params,
649
+ content_type="application/json",
650
+ )
651
+ response.raise_for_status()
652
+ if (
653
+ response.status_code == 204
654
+ or not response.content
655
+ or not response.text.strip()
656
+ ):
657
+ return None
658
+ try:
659
+ return response.json()
660
+ except ValueError:
661
+ return None
662
+
663
+ def update_account_stage(
664
+ self, account_ids_: list[str], account_stage_id: str
665
+ ) -> dict[str, Any]:
666
+ """
667
+ Updates multiple account records in bulk by their specified IDs, assigning each to the given account stage ID.
668
+
669
+ Args:
670
+ account_ids_ (array): The Apollo ID(s) of the account(s) to update; obtain these IDs by using the Search for Accounts endpoint and referencing each account’s `id` value.
671
+ account_stage_id (string): Specify the Apollo account stage ID to assign to accounts; find available IDs via the List Account Stages endpoint.
672
+
673
+ Returns:
674
+ dict[str, Any]: 200
675
+
676
+ Raises:
677
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
678
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
679
+
680
+ Tags:
681
+ Accounts
682
+ """
683
+ request_body_data = None
684
+ url = f"{self.base_url}/accounts/bulk_update"
685
+ query_params = {
686
+ k: v
687
+ for k, v in [
688
+ ("account_ids[]", account_ids_),
689
+ ("account_stage_id", account_stage_id),
690
+ ]
691
+ if v is not None
692
+ }
693
+ response = self._post(
694
+ url,
695
+ data=request_body_data,
696
+ params=query_params,
697
+ content_type="application/json",
698
+ )
699
+ response.raise_for_status()
700
+ if (
701
+ response.status_code == 204
702
+ or not response.content
703
+ or not response.text.strip()
704
+ ):
705
+ return None
706
+ try:
707
+ return response.json()
708
+ except ValueError:
709
+ return None
710
+
711
+ def update_account_ownership(
712
+ self, account_ids_: list[str], owner_id: str
713
+ ) -> dict[str, Any]:
714
+ """
715
+ Updates the owners of multiple accounts by assigning a specified owner ID to the given list of account IDs.
716
+
717
+ Args:
718
+ account_ids_ (array): The Apollo IDs of the accounts to assign new owners; obtain these IDs by using the Search for Accounts endpoint and referencing each account's `id` value.
719
+ owner_id (string): The owner_id is the unique identifier of the user in your Apollo team who will be assigned as the owner of the specified accounts; retrieve user IDs via the Get a List of Users endpoint.
720
+
721
+ Returns:
722
+ dict[str, Any]: 200
723
+
724
+ Raises:
725
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
726
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
727
+
728
+ Tags:
729
+ Accounts
730
+ """
731
+ request_body_data = None
732
+ url = f"{self.base_url}/accounts/update_owners"
733
+ query_params = {
734
+ k: v
735
+ for k, v in [("account_ids[]", account_ids_), ("owner_id", owner_id)]
736
+ if v is not None
737
+ }
738
+ response = self._post(
739
+ url,
740
+ data=request_body_data,
741
+ params=query_params,
742
+ content_type="application/json",
743
+ )
744
+ response.raise_for_status()
745
+ if (
746
+ response.status_code == 204
747
+ or not response.content
748
+ or not response.text.strip()
749
+ ):
750
+ return None
751
+ try:
752
+ return response.json()
753
+ except ValueError:
754
+ return None
755
+
756
+ def list_account_stages(self) -> dict[str, Any]:
757
+ """
758
+ Retrieves a list of account stages.
759
+
760
+ Returns:
761
+ dict[str, Any]: 200
762
+
763
+ Raises:
764
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
765
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
766
+
767
+ Tags:
768
+ Accounts
769
+ """
770
+ url = f"{self.base_url}/account_stages"
771
+ query_params = {}
772
+ response = self._get(url, params=query_params)
773
+ response.raise_for_status()
774
+ if (
775
+ response.status_code == 204
776
+ or not response.content
777
+ or not response.text.strip()
778
+ ):
779
+ return None
780
+ try:
781
+ return response.json()
782
+ except ValueError:
783
+ return None
784
+
785
+ def create_a_contact(
786
+ self,
787
+ first_name: str | None = None,
788
+ last_name: str | None = None,
789
+ organization_name: str | None = None,
790
+ title: str | None = None,
791
+ account_id: str | None = None,
792
+ email: str | None = None,
793
+ website_url: str | None = None,
794
+ label_names_: list[str] | None = None,
795
+ contact_stage_id: str | None = None,
796
+ present_raw_address: str | None = None,
797
+ direct_phone: str | None = None,
798
+ corporate_phone: str | None = None,
799
+ mobile_phone: str | None = None,
800
+ home_phone: str | None = None,
801
+ other_phone: str | None = None,
802
+ ) -> dict[str, Any]:
803
+ """
804
+ Creates a new contact with specified details such as name, organization, contact information, and labels.
805
+
806
+ Args:
807
+ first_name (string): The first name of the contact to be created, provided as a readable human name (e.g., "Tim"). This value identifies the contact in the system.
808
+ last_name (string): The last name of the contact to create, ideally a human-readable full surname.
809
+ organization_name (string): The current employer's exact company name for the contact, used to ensure correct assignment in the Apollo contact base; verify using the Organization Search endpoint if needed.
810
+ title (string): The current job title of the contact, such as 'senior research analyst'.
811
+ account_id (string): Unique Apollo ID for the account to assign the contact, retrieved from Organization Search or enrichment endpoints.
812
+ email (string): String: The email address of the contact; must be a valid, properly formatted email, e.g., example@email.com.
813
+ website_url (string): The full corporate website URL of the contact's current employer, including the domain (e.g., .com), without any subdirectories or social media links, to ensure accurate data enrichment.
814
+ label_names_ (array): Add contact to existing or new lists using any list name; unmatched names create new lists automatically. Example: "2024 big marketing conference attendees".
815
+ contact_stage_id (string): Specify the Apollo contact stage ID to assign the contact; if omitted, Apollo will assign a default stage per your settings. Example: 6095a710bd01d100a506d4ae
816
+ present_raw_address (string): The contact's personal location, which can include city, state, and country, matched to the closest predefined location for accurate identification (e.g., "Atlanta, United States").
817
+ direct_phone (string): Primary phone number for the contact. Enter in any format; Apollo sanitizes it. Examples: `555-303-1234`, `+44 7911 123456`.
818
+ corporate_phone (string): The direct work/office phone number for the contact (not the headquarters), which is sanitized and can be provided in any format; examples: 555-303-1234 or +44 7911 123456.
819
+ mobile_phone (string): Mobile phone number for the contact. Enter in any format; Apollo sanitizes and returns the standardized number.
820
+ home_phone (string): Enter the contact's home phone number in any format; Apollo sanitizes and returns the standardized value in the response.
821
+ other_phone (string): Specifies an alternative or unknown phone type for the contact; Apollo accepts any format and returns sanitized numbers in the response, e.g., '555-303-1234', '+44 7911 123456'.
822
+
823
+ Returns:
824
+ dict[str, Any]: 200
825
+
826
+ Raises:
827
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
828
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
829
+
830
+ Tags:
831
+ Contacts, important
832
+ """
833
+ request_body_data = None
834
+ url = f"{self.base_url}/contacts"
835
+ query_params = {
836
+ k: v
837
+ for k, v in [
838
+ ("first_name", first_name),
839
+ ("last_name", last_name),
840
+ ("organization_name", organization_name),
841
+ ("title", title),
842
+ ("account_id", account_id),
843
+ ("email", email),
844
+ ("website_url", website_url),
845
+ ("label_names[]", label_names_),
846
+ ("contact_stage_id", contact_stage_id),
847
+ ("present_raw_address", present_raw_address),
848
+ ("direct_phone", direct_phone),
849
+ ("corporate_phone", corporate_phone),
850
+ ("mobile_phone", mobile_phone),
851
+ ("home_phone", home_phone),
852
+ ("other_phone", other_phone),
853
+ ]
854
+ if v is not None
855
+ }
856
+ response = self._post(
857
+ url,
858
+ data=request_body_data,
859
+ params=query_params,
860
+ content_type="application/json",
861
+ )
862
+ response.raise_for_status()
863
+ if (
864
+ response.status_code == 204
865
+ or not response.content
866
+ or not response.text.strip()
867
+ ):
868
+ return None
869
+ try:
870
+ return response.json()
871
+ except ValueError:
872
+ return None
873
+
874
+ def update_a_contact(
875
+ self,
876
+ contact_id: str,
877
+ first_name: str | None = None,
878
+ last_name: str | None = None,
879
+ organization_name: str | None = None,
880
+ title: str | None = None,
881
+ account_id: str | None = None,
882
+ email: str | None = None,
883
+ website_url: str | None = None,
884
+ label_names_: list[str] | None = None,
885
+ contact_stage_id: str | None = None,
886
+ present_raw_address: str | None = None,
887
+ direct_phone: str | None = None,
888
+ corporate_phone: str | None = None,
889
+ mobile_phone: str | None = None,
890
+ home_phone: str | None = None,
891
+ other_phone: str | None = None,
892
+ ) -> dict[str, Any]:
893
+ """
894
+ Updates or replaces the details of a specific contact identified by contact_id using the provided parameters as new values for the contact record.
895
+
896
+ Args:
897
+ contact_id (string): contact_id
898
+ first_name (string): Update the contact's first name with a human-readable string representing their given name, such as "Tim".
899
+ last_name (string): Sets the contact's last name; expects a readable value (e.g. 'Zheng') for identification purposes.
900
+ organization_name (string): Update the contact's current employer name to match the exact company name in the Apollo database for accurate assignment. Example: `apollo`.
901
+ title (string): Specify the new job title for the contact, e.g., "senior research analyst."
902
+ account_id (string): Unique Apollo ID for updating the account assigned to a contact. Use Organization Search to find account IDs for enriched companies.
903
+ email (string): Update the contact's email address with a valid, unique email string (e.g., example@email.com); duplicate emails will cause an error response.
904
+ website_url (string): Update the full corporate website URL for the contact’s current employer, including the domain (e.g., .com) but excluding any subdirectories or social media links like LinkedIn, to ensure accurate data enrichment.
905
+ label_names_ (array): Update lists the contact belongs to in your Apollo account. If a list does not exist, Apollo creates it. Adding lists removes existing ones unless they are included again.
906
+ contact_stage_id (string): Specifies the Apollo contact stage ID; use this to assign or update the contact stage, available via List Contact Stages. Example: 6095a710bd01d100a506d4af.
907
+ present_raw_address (string): Set the contact's location by providing a city, state, and country, which will be matched to a predefined location.
908
+ direct_phone (string): Update the contact's primary phone number in any format; Apollo automatically sanitizes and standardizes the number, which is returned in the response for confirmation.
909
+ corporate_phone (string): Update the direct office phone number for the contact at their employer; Apollo sanitizes all formats and the cleaned number appears in the response.
910
+ mobile_phone (string): Sets or updates the contact's mobile phone number; accepts any format—Apollo sanitizes and returns the standardized version in the response.
911
+ home_phone (string): Specify the contact’s home phone number; Apollo accepts any format, sanitizes the input, and returns the clean version in the response.
912
+ other_phone (string): Update an alternative or unspecified phone number for the contact; Apollo automatically sanitizes any phone format, with the cleaned number shown in the response.
913
+
914
+ Returns:
915
+ dict[str, Any]: 200
916
+
917
+ Raises:
918
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
919
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
920
+
921
+ Tags:
922
+ Contacts
923
+ """
924
+ if contact_id is None:
925
+ raise ValueError("Missing required parameter 'contact_id'.")
926
+ request_body_data = None
927
+ url = f"{self.base_url}/contacts/{contact_id}"
928
+ query_params = {
929
+ k: v
930
+ for k, v in [
931
+ ("first_name", first_name),
932
+ ("last_name", last_name),
933
+ ("organization_name", organization_name),
934
+ ("title", title),
935
+ ("account_id", account_id),
936
+ ("email", email),
937
+ ("website_url", website_url),
938
+ ("label_names[]", label_names_),
939
+ ("contact_stage_id", contact_stage_id),
940
+ ("present_raw_address", present_raw_address),
941
+ ("direct_phone", direct_phone),
942
+ ("corporate_phone", corporate_phone),
943
+ ("mobile_phone", mobile_phone),
944
+ ("home_phone", home_phone),
945
+ ("other_phone", other_phone),
946
+ ]
947
+ if v is not None
948
+ }
949
+ response = self._put(
950
+ url,
951
+ data=request_body_data,
952
+ params=query_params,
953
+ content_type="application/json",
954
+ )
955
+ response.raise_for_status()
956
+ if (
957
+ response.status_code == 204
958
+ or not response.content
959
+ or not response.text.strip()
960
+ ):
961
+ return None
962
+ try:
963
+ return response.json()
964
+ except ValueError:
965
+ return None
966
+
967
+ def search_for_contacts(
968
+ self,
969
+ q_keywords: str | None = None,
970
+ contact_stage_ids_: list[str] | None = None,
971
+ sort_by_field: str | None = None,
972
+ sort_ascending: bool | None = None,
973
+ per_page: int | None = None,
974
+ page: int | None = None,
975
+ ) -> dict[str, Any]:
976
+ """
977
+ Searches contacts based on keywords, contact stage IDs, sorting, and pagination parameters, returning a filtered and sorted list of contacts.
978
+
979
+ Args:
980
+ q_keywords (string): Narrow search results by adding keywords such as names, job titles, companies, or email addresses.
981
+ contact_stage_ids_ (array): The Apollo IDs of the contact stages to include in the search; multiple IDs return contacts matching any listed stage combined with other search filters.
982
+ sort_by_field (string): Specify which field to sort results by: contact_last_activity_date, contact_email_last_opened_at, contact_email_last_clicked_at, contact_created_at, or contact_updated_at.
983
+ sort_ascending (boolean): Set to `true` to sort matching contacts in ascending order, but only if the `sort_by_field` parameter is specified; otherwise, sorting is ignored.
984
+ per_page (integer): Specifies the number of results to return per page, enhancing search result navigation and performance.
985
+ page (integer): Page size for search results; limits the number of results per page to improve performance.
986
+
987
+ Returns:
988
+ dict[str, Any]: 200
989
+
990
+ Raises:
991
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
992
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
993
+
994
+ Tags:
995
+ Contacts, important
996
+ """
997
+ request_body_data = None
998
+ url = f"{self.base_url}/contacts/search"
999
+ query_params = {
1000
+ k: v
1001
+ for k, v in [
1002
+ ("q_keywords", q_keywords),
1003
+ ("contact_stage_ids[]", contact_stage_ids_),
1004
+ ("sort_by_field", sort_by_field),
1005
+ ("sort_ascending", sort_ascending),
1006
+ ("per_page", per_page),
1007
+ ("page", page),
1008
+ ]
1009
+ if v is not None
1010
+ }
1011
+ response = self._post(
1012
+ url,
1013
+ data=request_body_data,
1014
+ params=query_params,
1015
+ content_type="application/json",
1016
+ )
1017
+ response.raise_for_status()
1018
+ if (
1019
+ response.status_code == 204
1020
+ or not response.content
1021
+ or not response.text.strip()
1022
+ ):
1023
+ return None
1024
+ try:
1025
+ return response.json()
1026
+ except ValueError:
1027
+ return None
1028
+
1029
+ def update_contact_stage(
1030
+ self, contact_ids_: list[str], contact_stage_id: str
1031
+ ) -> dict[str, Any]:
1032
+ """
1033
+ Updates the stage of multiple contacts by specifying their IDs and the new contact stage ID via a POST request.
1034
+
1035
+ Args:
1036
+ contact_ids_ (array): The Apollo contact IDs to update, provided as an array of strings; obtain these IDs from the Search for Contacts endpoint's `id` field. Example: `66e34b81740c50074e3d1bd4`
1037
+ contact_stage_id (string): The Apollo ID of the contact stage to assign to contacts; retrieve valid IDs via the List Contact Stages endpoint. Example: `6095a710bd01d100a506d4af`.
1038
+
1039
+ Returns:
1040
+ dict[str, Any]: 200
1041
+
1042
+ Raises:
1043
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1044
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1045
+
1046
+ Tags:
1047
+ Contacts
1048
+ """
1049
+ request_body_data = None
1050
+ url = f"{self.base_url}/contacts/update_stages"
1051
+ query_params = {
1052
+ k: v
1053
+ for k, v in [
1054
+ ("contact_ids[]", contact_ids_),
1055
+ ("contact_stage_id", contact_stage_id),
1056
+ ]
1057
+ if v is not None
1058
+ }
1059
+ response = self._post(
1060
+ url,
1061
+ data=request_body_data,
1062
+ params=query_params,
1063
+ content_type="application/json",
1064
+ )
1065
+ response.raise_for_status()
1066
+ if (
1067
+ response.status_code == 204
1068
+ or not response.content
1069
+ or not response.text.strip()
1070
+ ):
1071
+ return None
1072
+ try:
1073
+ return response.json()
1074
+ except ValueError:
1075
+ return None
1076
+
1077
+ def update_contact_ownership(
1078
+ self, contact_ids_: list[str], owner_id: str
1079
+ ) -> dict[str, Any]:
1080
+ """
1081
+ Updates the owners of specified contacts by assigning a new owner ID to the provided list of contact IDs.
1082
+
1083
+ Args:
1084
+ contact_ids_ (array): The Apollo contact IDs to update ownership for; provide one or more IDs obtained from the Search for Contacts endpoint to assign new owners.
1085
+ owner_id (string): Specifies the Apollo account user ID to assign as owner for the contacts; find user IDs via the Get List of Users endpoint. Example: 66302798d03b9601c7934ebf.
1086
+
1087
+ Returns:
1088
+ dict[str, Any]: 200
1089
+
1090
+ Raises:
1091
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1092
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1093
+
1094
+ Tags:
1095
+ Contacts
1096
+ """
1097
+ request_body_data = None
1098
+ url = f"{self.base_url}/contacts/update_owners"
1099
+ query_params = {
1100
+ k: v
1101
+ for k, v in [("contact_ids[]", contact_ids_), ("owner_id", owner_id)]
1102
+ if v is not None
1103
+ }
1104
+ response = self._post(
1105
+ url,
1106
+ data=request_body_data,
1107
+ params=query_params,
1108
+ content_type="application/json",
1109
+ )
1110
+ response.raise_for_status()
1111
+ if (
1112
+ response.status_code == 204
1113
+ or not response.content
1114
+ or not response.text.strip()
1115
+ ):
1116
+ return None
1117
+ try:
1118
+ return response.json()
1119
+ except ValueError:
1120
+ return None
1121
+
1122
+ def list_contact_stages(self) -> Any:
1123
+ """
1124
+ Retrieves a list of all available contact stage IDs from the Apollo account[2][4].
1125
+
1126
+ Returns:
1127
+ Any: 200
1128
+
1129
+ Raises:
1130
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1131
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1132
+
1133
+ Tags:
1134
+ Contacts
1135
+ """
1136
+ url = f"{self.base_url}/contact_stages"
1137
+ query_params = {}
1138
+ response = self._get(url, params=query_params)
1139
+ response.raise_for_status()
1140
+ if (
1141
+ response.status_code == 204
1142
+ or not response.content
1143
+ or not response.text.strip()
1144
+ ):
1145
+ return None
1146
+ try:
1147
+ return response.json()
1148
+ except ValueError:
1149
+ return None
1150
+
1151
+ def create_deal(
1152
+ self,
1153
+ name: str,
1154
+ owner_id: str | None = None,
1155
+ account_id: str | None = None,
1156
+ amount: str | None = None,
1157
+ opportunity_stage_id: str | None = None,
1158
+ closed_date: str | None = None,
1159
+ ) -> dict[str, Any]:
1160
+ """
1161
+ Creates a new opportunity with specified details such as name, owner, account, amount, stage, and closed date.
1162
+
1163
+ Args:
1164
+ name (string): Specify a short, descriptive, human-readable name for the opportunity or deal being created, such as "Massive Q3 Deal."
1165
+ owner_id (string): Specify the unique Apollo user ID (found via Get Users endpoint) responsible for the opportunity as the deal owner.
1166
+ account_id (string): The unique Apollo account ID for the company targeted in this deal; retrieve via Organization Search as organization_id—example: 5e66b6381e05b4008c8331b8.
1167
+ amount (string): The monetary value of the deal to create, entered as a numeric amount without commas or currency symbols; the currency is set automatically from your Apollo account settings.
1168
+ opportunity_stage_id (string): The unique identifier for the deal stage in your team's Apollo account; use the List Deal Stages endpoint to retrieve valid IDs for this parameter.
1169
+ closed_date (string): The estimated close date for the deal, in YYYY-MM-DD format.
1170
+
1171
+ Returns:
1172
+ dict[str, Any]: 200
1173
+
1174
+ Raises:
1175
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1176
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1177
+
1178
+ Tags:
1179
+ Deals
1180
+ """
1181
+ request_body_data = None
1182
+ url = f"{self.base_url}/opportunities"
1183
+ query_params = {
1184
+ k: v
1185
+ for k, v in [
1186
+ ("name", name),
1187
+ ("owner_id", owner_id),
1188
+ ("account_id", account_id),
1189
+ ("amount", amount),
1190
+ ("opportunity_stage_id", opportunity_stage_id),
1191
+ ("closed_date", closed_date),
1192
+ ]
1193
+ if v is not None
1194
+ }
1195
+ response = self._post(
1196
+ url,
1197
+ data=request_body_data,
1198
+ params=query_params,
1199
+ content_type="application/json",
1200
+ )
1201
+ response.raise_for_status()
1202
+ if (
1203
+ response.status_code == 204
1204
+ or not response.content
1205
+ or not response.text.strip()
1206
+ ):
1207
+ return None
1208
+ try:
1209
+ return response.json()
1210
+ except ValueError:
1211
+ return None
1212
+
1213
+ def list_all_deals(
1214
+ self,
1215
+ sort_by_field: str | None = None,
1216
+ page: int | None = None,
1217
+ per_page: int | None = None,
1218
+ ) -> dict[str, Any]:
1219
+ """
1220
+ Searches and retrieves a paginated list of opportunities with optional sorting by a specified field.
1221
+
1222
+ Args:
1223
+ sort_by_field (string): Sort opportunities by one of the following: amount for highest deal values first, is_closed for closed deals first, or is_won for deals marked as won first.
1224
+ page (integer): The page query parameter specifies which page of results to retrieve when searching opportunities; use with `per_page` to paginate and optimize response performance.
1225
+ per_page (integer): The number of results returned per page in the search response; limiting this improves performance. Use the `page` parameter to access additional pages. Example: `10`.
1226
+
1227
+ Returns:
1228
+ dict[str, Any]: 200
1229
+
1230
+ Raises:
1231
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1232
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1233
+
1234
+ Tags:
1235
+ Deals, important
1236
+ """
1237
+ url = f"{self.base_url}/opportunities/search"
1238
+ query_params = {
1239
+ k: v
1240
+ for k, v in [
1241
+ ("sort_by_field", sort_by_field),
1242
+ ("page", page),
1243
+ ("per_page", per_page),
1244
+ ]
1245
+ if v is not None
1246
+ }
1247
+ response = self._get(url, params=query_params)
1248
+ response.raise_for_status()
1249
+ if (
1250
+ response.status_code == 204
1251
+ or not response.content
1252
+ or not response.text.strip()
1253
+ ):
1254
+ return None
1255
+ try:
1256
+ return response.json()
1257
+ except ValueError:
1258
+ return None
1259
+
1260
+ def update_deal(
1261
+ self,
1262
+ opportunity_id: str,
1263
+ owner_id: str | None = None,
1264
+ name: str | None = None,
1265
+ amount: str | None = None,
1266
+ opportunity_stage_id: str | None = None,
1267
+ closed_date: str | None = None,
1268
+ is_closed: bool | None = None,
1269
+ is_won: bool | None = None,
1270
+ source: str | None = None,
1271
+ account_id: str | None = None,
1272
+ ) -> dict[str, Any]:
1273
+ """
1274
+ Updates specific fields of an opportunity resource identified by opportunity_id using a PATCH request[2][4][5].
1275
+
1276
+ Args:
1277
+ opportunity_id (string): opportunity_id
1278
+ owner_id (string): The ID of the user within your Apollo team to assign as the new owner of the deal; use the List Users endpoint to find valid user IDs.
1279
+ name (string): Update the deal’s name with a clear, human-readable title that identifies the opportunity, such as "Massive Q3 Deal."
1280
+ amount (string): The monetary value of the deal to update; enter a numeric value without commas or currency symbols—currency is set by your Apollo account settings. Example: 55123478 represents $55,123,478 if USD.
1281
+ opportunity_stage_id (string): Unique ID of the deal stage to update an opportunity's status. Replace with a different ID to change the stage.
1282
+ closed_date (string): Update the estimated close date for the opportunity, which can be any past or future date, formatted as YYYY-MM-DD (e.g., 2025-10-30).
1283
+ is_closed (boolean): Set to true to mark the opportunity as closed, or omit/use false to keep it open.
1284
+ is_won (boolean): Set this parameter to `true` in the query to mark the opportunity as won and update the deal status accordingly.
1285
+ source (string): Update the source of the deal, e.g., '2024 InfoSec Conference', overriding the default 'api' source for API-created deals.
1286
+ account_id (string): Specify the unique account ID to associate or update the company linked to this opportunity; find IDs using the Organization Search endpoint.
1287
+
1288
+ Returns:
1289
+ dict[str, Any]: 200
1290
+
1291
+ Raises:
1292
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1293
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1294
+
1295
+ Tags:
1296
+ Deals
1297
+ """
1298
+ if opportunity_id is None:
1299
+ raise ValueError("Missing required parameter 'opportunity_id'.")
1300
+ request_body_data = None
1301
+ url = f"{self.base_url}/opportunities/{opportunity_id}"
1302
+ query_params = {
1303
+ k: v
1304
+ for k, v in [
1305
+ ("owner_id", owner_id),
1306
+ ("name", name),
1307
+ ("amount", amount),
1308
+ ("opportunity_stage_id", opportunity_stage_id),
1309
+ ("closed_date", closed_date),
1310
+ ("is_closed", is_closed),
1311
+ ("is_won", is_won),
1312
+ ("source", source),
1313
+ ("account_id", account_id),
1314
+ ]
1315
+ if v is not None
1316
+ }
1317
+ response = self._patch(url, data=request_body_data, params=query_params)
1318
+ response.raise_for_status()
1319
+ if (
1320
+ response.status_code == 204
1321
+ or not response.content
1322
+ or not response.text.strip()
1323
+ ):
1324
+ return None
1325
+ try:
1326
+ return response.json()
1327
+ except ValueError:
1328
+ return None
1329
+
1330
+ def list_deal_stages(self) -> dict[str, Any]:
1331
+ """
1332
+ Retrieves a list of opportunity stages representing the different phases in the sales pipeline.
1333
+
1334
+ Returns:
1335
+ dict[str, Any]: 200
1336
+
1337
+ Raises:
1338
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1339
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1340
+
1341
+ Tags:
1342
+ Deals
1343
+ """
1344
+ url = f"{self.base_url}/opportunity_stages"
1345
+ query_params = {}
1346
+ response = self._get(url, params=query_params)
1347
+ response.raise_for_status()
1348
+ if (
1349
+ response.status_code == 204
1350
+ or not response.content
1351
+ or not response.text.strip()
1352
+ ):
1353
+ return None
1354
+ try:
1355
+ return response.json()
1356
+ except ValueError:
1357
+ return None
1358
+
1359
+ def add_contacts_to_sequence(
1360
+ self,
1361
+ sequence_id: str,
1362
+ emailer_campaign_id: str,
1363
+ contact_ids_: list[str],
1364
+ send_email_from_email_account_id: str,
1365
+ sequence_no_email: bool | None = None,
1366
+ sequence_unverified_email: bool | None = None,
1367
+ sequence_job_change: bool | None = None,
1368
+ sequence_active_in_other_campaigns: bool | None = None,
1369
+ sequence_finished_in_other_campaigns: bool | None = None,
1370
+ user_id: str | None = None,
1371
+ ) -> dict[str, Any]:
1372
+ """
1373
+ Adds specified contact IDs to an email campaign sequence, configuring how and when emails are sent to each contact and supporting various filtering options.
1374
+
1375
+ Args:
1376
+ sequence_id (string): sequence_id
1377
+ emailer_campaign_id (string): The emailer_campaign_id query parameter must match the sequence_id path parameter and represents the unique identifier of the email campaign to which contacts are being added.
1378
+ contact_ids_ (array): Apollo IDs of contacts to add to the sequence. Use the Search for Contacts endpoint to find IDs.
1379
+ send_email_from_email_account_id (string): The Apollo ID of the email account used to send emails to contacts added to the sequence; obtain this ID from the Get a List of Email Accounts endpoint.
1380
+ sequence_no_email (boolean): Add contacts to the sequence even if they lack an email address by setting this to `true`.
1381
+ sequence_unverified_email (boolean): Indicates whether to allow adding contacts with unverified email addresses to the sequence.
1382
+ sequence_job_change (boolean): Set to `true` to add contacts to the email sequence even if they have recently changed jobs, overriding any default restrictions on re-adding such contacts.
1383
+ sequence_active_in_other_campaigns (boolean): When true, allows adding contacts even if they are already in other sequences, regardless of those sequences’ status (active or paused).
1384
+ sequence_finished_in_other_campaigns (boolean): Set to `true` to add contacts to this sequence even if they have completed a different sequence and are marked as finished there.
1385
+ user_id (string): The user_id query parameter specifies the ID of the Apollo team user performing the action to add contacts to a sequence, which appears in the sequence's activity log to identify who added the contacts.
1386
+
1387
+ Returns:
1388
+ dict[str, Any]: 200
1389
+
1390
+ Raises:
1391
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1392
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1393
+
1394
+ Tags:
1395
+ Contacts
1396
+ """
1397
+ if sequence_id is None:
1398
+ raise ValueError("Missing required parameter 'sequence_id'.")
1399
+ request_body_data = None
1400
+ url = f"{self.base_url}/emailer_campaigns/{sequence_id}/add_contact_ids"
1401
+ query_params = {
1402
+ k: v
1403
+ for k, v in [
1404
+ ("emailer_campaign_id", emailer_campaign_id),
1405
+ ("contact_ids[]", contact_ids_),
1406
+ ("send_email_from_email_account_id", send_email_from_email_account_id),
1407
+ ("sequence_no_email", sequence_no_email),
1408
+ ("sequence_unverified_email", sequence_unverified_email),
1409
+ ("sequence_job_change", sequence_job_change),
1410
+ (
1411
+ "sequence_active_in_other_campaigns",
1412
+ sequence_active_in_other_campaigns,
1413
+ ),
1414
+ (
1415
+ "sequence_finished_in_other_campaigns",
1416
+ sequence_finished_in_other_campaigns,
1417
+ ),
1418
+ ("user_id", user_id),
1419
+ ]
1420
+ if v is not None
1421
+ }
1422
+ response = self._post(
1423
+ url,
1424
+ data=request_body_data,
1425
+ params=query_params,
1426
+ content_type="application/json",
1427
+ )
1428
+ response.raise_for_status()
1429
+ if (
1430
+ response.status_code == 204
1431
+ or not response.content
1432
+ or not response.text.strip()
1433
+ ):
1434
+ return None
1435
+ try:
1436
+ return response.json()
1437
+ except ValueError:
1438
+ return None
1439
+
1440
+ def update_contact_status_sequence(
1441
+ self, emailer_campaign_ids_: list[str], contact_ids_: list[str], mode: str
1442
+ ) -> dict[str, Any]:
1443
+ """
1444
+ Posts a request to remove or stop specified contact IDs from given emailer campaign IDs based on the selected mode.
1445
+
1446
+ Args:
1447
+ emailer_campaign_ids_ (array): The Apollo sequence IDs to update contact statuses in; providing multiple IDs updates contacts across all specified sequences. Use the Search for Sequences endpoint to find these IDs.
1448
+ contact_ids_ (array): Specify the Apollo IDs of contacts to update their sequence status. Obtain IDs via the Search for Contacts endpoint.
1449
+ mode (string): Choose one option to update contacts' sequence status: `mark_as_finished` to mark as completed, `remove` to delete from the sequence, or `stop` to pause their progression.
1450
+
1451
+ Returns:
1452
+ dict[str, Any]: 200
1453
+
1454
+ Raises:
1455
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1456
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1457
+
1458
+ Tags:
1459
+ Contacts
1460
+ """
1461
+ request_body_data = None
1462
+ url = f"{self.base_url}/emailer_campaigns/remove_or_stop_contact_ids"
1463
+ query_params = {
1464
+ k: v
1465
+ for k, v in [
1466
+ ("emailer_campaign_ids[]", emailer_campaign_ids_),
1467
+ ("contact_ids[]", contact_ids_),
1468
+ ("mode", mode),
1469
+ ]
1470
+ if v is not None
1471
+ }
1472
+ response = self._post(
1473
+ url,
1474
+ data=request_body_data,
1475
+ params=query_params,
1476
+ content_type="application/json",
1477
+ )
1478
+ response.raise_for_status()
1479
+ if (
1480
+ response.status_code == 204
1481
+ or not response.content
1482
+ or not response.text.strip()
1483
+ ):
1484
+ return None
1485
+ try:
1486
+ return response.json()
1487
+ except ValueError:
1488
+ return None
1489
+
1490
+ def create_task(
1491
+ self,
1492
+ user_id: str,
1493
+ contact_ids_: list[str],
1494
+ priority: str,
1495
+ due_at: str,
1496
+ type: str,
1497
+ status: str,
1498
+ note: str | None = None,
1499
+ ) -> Any:
1500
+ """
1501
+ Creates multiple tasks in bulk with specified user, contact IDs, priority, due date, type, status, and optional note parameters.
1502
+
1503
+ Args:
1504
+ user_id (string): The user_id query parameter specifies the unique identifier of the Apollo team member who will own and take action on the created tasks; retrieve user IDs from the Get a List of Users endpoint.
1505
+ contact_ids_ (array): Apollo IDs of contacts to receive the action; multiple IDs create separate tasks with the same details.
1506
+ priority (string): Specify the priority level for each task being created in bulk; valid values are "high," "medium," or "low" to indicate urgency.
1507
+ due_at (string): The full date and time when the task is due, in ISO 8601 format. Use GMT by default or specify a time zone offset (e.g., `2025-02-15T08:10:30Z`, `2025-03-25T10:15:30+05:00`).
1508
+ type (string): Specify the task type to clarify the action required: `call` to call contacts, `outreach_manual_email` to email, `linkedin_step_connect` to send connection invites, `linkedin_step_message` to message on LinkedIn, `linkedin_step_view_profile` to view
1509
+ status (string): The status of the task being created. Use `scheduled` for future tasks, `completed` for finished tasks, or `archived` for completed tasks no longer needed.
1510
+ note (string): Optional task note: human-readable description to give context for the action required. Example: "Contact interested in Sequences; discuss details."
1511
+
1512
+ Returns:
1513
+ Any: 200
1514
+
1515
+ Raises:
1516
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1517
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1518
+
1519
+ Tags:
1520
+ Tasks
1521
+ """
1522
+ request_body_data = None
1523
+ url = f"{self.base_url}/tasks/bulk_create"
1524
+ query_params = {
1525
+ k: v
1526
+ for k, v in [
1527
+ ("user_id", user_id),
1528
+ ("contact_ids[]", contact_ids_),
1529
+ ("priority", priority),
1530
+ ("due_at", due_at),
1531
+ ("type", type),
1532
+ ("status", status),
1533
+ ("note", note),
1534
+ ]
1535
+ if v is not None
1536
+ }
1537
+ response = self._post(
1538
+ url,
1539
+ data=request_body_data,
1540
+ params=query_params,
1541
+ content_type="application/json",
1542
+ )
1543
+ response.raise_for_status()
1544
+ if (
1545
+ response.status_code == 204
1546
+ or not response.content
1547
+ or not response.text.strip()
1548
+ ):
1549
+ return None
1550
+ try:
1551
+ return response.json()
1552
+ except ValueError:
1553
+ return None
1554
+
1555
+ def search_tasks(
1556
+ self,
1557
+ sort_by_field: str | None = None,
1558
+ open_factor_names_: list[str] | None = None,
1559
+ page: int | None = None,
1560
+ per_page: int | None = None,
1561
+ ) -> dict[str, Any]:
1562
+ """
1563
+ Searches for tasks using specified parameters and returns a paginated list of results, allowing users to sort by a field and filter by open factor names.
1564
+
1565
+ Args:
1566
+ sort_by_field (string): Specify field to sort tasks: 'task_due_at' (most future first) or 'task_priority' (highest first).
1567
+ open_factor_names_ (array): Enter `task_types` to receive a count of tasks grouped by each task type; the response will include a `task_types` array with counts for each type.
1568
+ page (integer): The page query parameter specifies which page of Apollo data to retrieve, used with per_page to paginate results and optimize search performance; for example, 4.
1569
+ per_page (integer): The number of search results returned per page to improve response performance; use the `page` parameter to access additional pages. Example: `10`.
1570
+
1571
+ Returns:
1572
+ dict[str, Any]: 200
1573
+
1574
+ Raises:
1575
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1576
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1577
+
1578
+ Tags:
1579
+ Tasks
1580
+ """
1581
+ request_body_data = None
1582
+ url = f"{self.base_url}/tasks/search"
1583
+ query_params = {
1584
+ k: v
1585
+ for k, v in [
1586
+ ("sort_by_field", sort_by_field),
1587
+ ("open_factor_names[]", open_factor_names_),
1588
+ ("page", page),
1589
+ ("per_page", per_page),
1590
+ ]
1591
+ if v is not None
1592
+ }
1593
+ response = self._post(
1594
+ url,
1595
+ data=request_body_data,
1596
+ params=query_params,
1597
+ content_type="application/json",
1598
+ )
1599
+ response.raise_for_status()
1600
+ if (
1601
+ response.status_code == 204
1602
+ or not response.content
1603
+ or not response.text.strip()
1604
+ ):
1605
+ return None
1606
+ try:
1607
+ return response.json()
1608
+ except ValueError:
1609
+ return None
1610
+
1611
+ def get_a_list_of_users(
1612
+ self, page: int | None = None, per_page: int | None = None
1613
+ ) -> dict[str, Any]:
1614
+ """
1615
+ Searches for users with optional pagination parameters to specify the page number and number of results per page.
1616
+
1617
+ Args:
1618
+ page (integer): The page number of results to retrieve in the search, used with `per_page` to paginate and improve response performance; for example, `4`.
1619
+ per_page (integer): The number of search results returned per page to control response size and improve performance; use the `page` parameter to access additional pages. Example: 10.
1620
+
1621
+ Returns:
1622
+ dict[str, Any]: 200
1623
+
1624
+ Raises:
1625
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1626
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1627
+
1628
+ Tags:
1629
+ Users
1630
+ """
1631
+ url = f"{self.base_url}/users/search"
1632
+ query_params = {
1633
+ k: v for k, v in [("page", page), ("per_page", per_page)] if v is not None
1634
+ }
1635
+ response = self._get(url, params=query_params)
1636
+ response.raise_for_status()
1637
+ if (
1638
+ response.status_code == 204
1639
+ or not response.content
1640
+ or not response.text.strip()
1641
+ ):
1642
+ return None
1643
+ try:
1644
+ return response.json()
1645
+ except ValueError:
1646
+ return None
1647
+
1648
+ def get_a_list_of_email_accounts(self) -> dict[str, Any]:
1649
+ """
1650
+ Retrieves a list of all available email accounts and their summary information.
1651
+
1652
+ Returns:
1653
+ dict[str, Any]: 200
1654
+
1655
+ Raises:
1656
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1657
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1658
+
1659
+ Tags:
1660
+ Email Accounts
1661
+ """
1662
+ url = f"{self.base_url}/email_accounts"
1663
+ query_params = {}
1664
+ response = self._get(url, params=query_params)
1665
+ response.raise_for_status()
1666
+ if (
1667
+ response.status_code == 204
1668
+ or not response.content
1669
+ or not response.text.strip()
1670
+ ):
1671
+ return None
1672
+ try:
1673
+ return response.json()
1674
+ except ValueError:
1675
+ return None
1676
+
1677
+ def get_a_list_of_all_liststags(self) -> list[Any]:
1678
+ """
1679
+ Retrieves a list of labels.
1680
+
1681
+ Returns:
1682
+ list[Any]: 200
1683
+
1684
+ Raises:
1685
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1686
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1687
+
1688
+ Tags:
1689
+ Labels
1690
+ """
1691
+ url = f"{self.base_url}/labels"
1692
+ query_params = {}
1693
+ response = self._get(url, params=query_params)
1694
+ response.raise_for_status()
1695
+ if (
1696
+ response.status_code == 204
1697
+ or not response.content
1698
+ or not response.text.strip()
1699
+ ):
1700
+ return None
1701
+ try:
1702
+ return response.json()
1703
+ except ValueError:
1704
+ return None
1705
+
1706
+ def get_a_list_of_all_custom_fields(self) -> dict[str, Any]:
1707
+ """
1708
+ Retrieves a list of all typed custom fields configured in the system.
1709
+
1710
+ Returns:
1711
+ dict[str, Any]: 200
1712
+
1713
+ Raises:
1714
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1715
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1716
+
1717
+ Tags:
1718
+ Custom Fields
1719
+ """
1720
+ url = f"{self.base_url}/typed_custom_fields"
1721
+ query_params = {}
1722
+ response = self._get(url, params=query_params)
1723
+ response.raise_for_status()
1724
+ if (
1725
+ response.status_code == 204
1726
+ or not response.content
1727
+ or not response.text.strip()
1728
+ ):
1729
+ return None
1730
+ try:
1731
+ return response.json()
1732
+ except ValueError:
1733
+ return None
1734
+
1735
+ def view_deal(self, opportunity_id: str) -> dict[str, Any]:
1736
+ """
1737
+ View Deal by opportunity_id
1738
+
1739
+ Args:
1740
+ opportunity_id (string): opportunity_id
1741
+
1742
+ Returns:
1743
+ dict[str, Any]: 200
1744
+
1745
+ Raises:
1746
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1747
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1748
+ """
1749
+ if opportunity_id is None:
1750
+ raise ValueError("Missing required parameter 'opportunity_id'.")
1751
+ url = f"{self.base_url}/opportunities/{opportunity_id}"
1752
+ query_params = {}
1753
+ response = self._get(url, params=query_params)
1754
+ response.raise_for_status()
1755
+ if (
1756
+ response.status_code == 204
1757
+ or not response.content
1758
+ or not response.text.strip()
1759
+ ):
1760
+ return None
1761
+ try:
1762
+ return response.json()
1763
+ except ValueError:
1764
+ return None
1765
+
1766
+ def search_for_sequences(
1767
+ self,
1768
+ q_name: str | None = None,
1769
+ page: str | None = None,
1770
+ per_page: str | None = None,
1771
+ ) -> dict[str, Any]:
1772
+ """
1773
+ Search for Sequences by name
1774
+
1775
+ Args:
1776
+ q_name (string): Add keywords to narrow the search of the sequences in your team's Apollo account. <br><br>Keywords should directly match at least part of a sequence's name. For example, searching the keyword `marketing` might return the result `NY Marketing Sequence`, but not `NY Marketer Conference 2025 attendees`. <br><br>This parameter only searches sequence names, not other sequence fields. <br><br>Example: `marketing conference attendees`
1777
+ page (string): The page number of the Apollo data that you want to retrieve. <br><br>Use this parameter in combination with the `per_page` parameter to make search results for navigable and improve the performance of the endpoint. <br><br>Example: `4`
1778
+ per_page (string): The number of search results that should be returned for each page. Limited the number of results per page improves the endpoint's performance. <br><br>Use the `page` parameter to search the different pages of data. <br><br>Example: `10`
1779
+
1780
+ Returns:
1781
+ dict[str, Any]: 200
1782
+
1783
+ Raises:
1784
+ HTTPError: Raised when the API request fails (e.g., non-2XX status code).
1785
+ JSONDecodeError: Raised if the response body cannot be parsed as JSON.
1786
+ """
1787
+ request_body_data = None
1788
+ url = f"{self.base_url}/emailer_campaigns/search"
1789
+ query_params = {
1790
+ k: v
1791
+ for k, v in [("q_name", q_name), ("page", page), ("per_page", per_page)]
1792
+ if v is not None
1793
+ }
1794
+ response = self._post(
1795
+ url,
1796
+ data=request_body_data,
1797
+ params=query_params,
1798
+ content_type="application/json",
1799
+ )
1800
+ response.raise_for_status()
1801
+ if (
1802
+ response.status_code == 204
1803
+ or not response.content
1804
+ or not response.text.strip()
1805
+ ):
1806
+ return None
1807
+ try:
1808
+ return response.json()
1809
+ except ValueError:
1810
+ return None
1811
+
1812
+ def list_tools(self):
1813
+ return [
1814
+ self.people_enrichment,
1815
+ self.bulk_people_enrichment,
1816
+ self.organization_enrichment,
1817
+ self.bulk_organization_enrichment,
1818
+ self.people_search,
1819
+ self.organization_search,
1820
+ self.organization_jobs_postings,
1821
+ self.create_an_account,
1822
+ self.update_an_account,
1823
+ self.search_for_accounts,
1824
+ self.update_account_stage,
1825
+ self.update_account_ownership,
1826
+ self.list_account_stages,
1827
+ self.create_a_contact,
1828
+ self.update_a_contact,
1829
+ self.search_for_contacts,
1830
+ self.update_contact_stage,
1831
+ self.update_contact_ownership,
1832
+ self.list_contact_stages,
1833
+ self.create_deal,
1834
+ self.list_all_deals,
1835
+ self.update_deal,
1836
+ self.list_deal_stages,
1837
+ self.add_contacts_to_sequence,
1838
+ self.update_contact_status_sequence,
1839
+ self.create_task,
1840
+ self.search_tasks,
1841
+ self.get_a_list_of_users,
1842
+ self.get_a_list_of_email_accounts,
1843
+ self.get_a_list_of_all_liststags,
1844
+ self.get_a_list_of_all_custom_fields,
1845
+ self.view_deal,
1846
+ self.search_for_sequences,
1847
+ ]