Prak2005 commited on
Commit
fe6d5d0
Β·
verified Β·
1 Parent(s): 6448462

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +321 -99
app.py CHANGED
@@ -1,4 +1,3 @@
1
- # app.py - Main application file for Hugging Face Space
2
  import gradio as gr
3
  import requests
4
  import json
@@ -8,9 +7,17 @@ from typing import Dict, List, Optional
8
  import time
9
  import random
10
 
 
 
 
 
11
  class ApolloDataFetcher:
 
 
 
 
12
  def __init__(self, api_key: str = None):
13
- # Try to get API key from environment (Hugging Face secrets) or parameter
14
  self.api_key = api_key or os.getenv("APOLLO_API_KEY")
15
  self.base_url = "https://api.apollo.io/v1"
16
  self.headers = {
@@ -20,60 +27,60 @@ class ApolloDataFetcher:
20
  if self.api_key and self.api_key != "demo":
21
  self.headers["X-Api-Key"] = self.api_key
22
 
 
 
 
 
23
  def search_people(self, query: str, limit: int = 10) -> Dict:
24
  """Search for people using Apollo.io API"""
25
  if not self.api_key or self.api_key == "demo":
26
  return self._generate_demo_people_data(query, limit)
27
 
28
- endpoint = f"{self.base_url}/mixed_people/search"
29
  payload = {
30
  "q_keywords": query,
31
  "page": 1,
32
- "per_page": min(limit, 25) # Apollo limits to 25 per page
 
33
  }
34
 
35
  try:
36
  response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
37
- if response.status_code == 200:
38
- return response.json()
39
- elif response.status_code == 401:
40
- return {"error": "Invalid API key. Please check your Apollo.io API key."}
41
- elif response.status_code == 429:
42
- return {"error": "Rate limit exceeded. Please try again later."}
43
- else:
44
- return {"error": f"API Error: {response.status_code} - {response.text}"}
45
  except requests.exceptions.Timeout:
46
  return {"error": "Request timeout. Please try again."}
47
  except Exception as e:
48
  return {"error": f"Request failed: {str(e)}"}
49
 
 
 
 
 
50
  def search_companies(self, query: str, limit: int = 10) -> Dict:
51
  """Search for companies using Apollo.io API"""
52
  if not self.api_key or self.api_key == "demo":
53
  return self._generate_demo_company_data(query, limit)
54
 
55
- endpoint = f"{self.base_url}/mixed_companies/search"
56
  payload = {
57
  "q_keywords": query,
58
  "page": 1,
59
- "per_page": min(limit, 25)
 
60
  }
61
 
62
  try:
63
  response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
64
- if response.status_code == 200:
65
- return response.json()
66
- elif response.status_code == 401:
67
- return {"error": "Invalid API key. Please check your Apollo.io API key."}
68
- elif response.status_code == 429:
69
- return {"error": "Rate limit exceeded. Please try again later."}
70
- else:
71
- return {"error": f"API Error: {response.status_code} - {response.text}"}
72
  except requests.exceptions.Timeout:
73
  return {"error": "Request timeout. Please try again."}
74
  except Exception as e:
75
  return {"error": f"Request failed: {str(e)}"}
76
 
 
 
 
 
77
  def enrich_person(self, email: str) -> Dict:
78
  """Enrich person data by email"""
79
  if not self.api_key or self.api_key == "demo":
@@ -84,26 +91,72 @@ class ApolloDataFetcher:
84
 
85
  try:
86
  response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
87
- if response.status_code == 200:
88
- return response.json()
89
- elif response.status_code == 401:
90
- return {"error": "Invalid API key. Please check your Apollo.io API key."}
91
- elif response.status_code == 429:
92
- return {"error": "Rate limit exceeded. Please try again later."}
93
- else:
94
- return {"error": f"API Error: {response.status_code} - {response.text}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  except requests.exceptions.Timeout:
96
  return {"error": "Request timeout. Please try again."}
97
  except Exception as e:
98
  return {"error": f"Request failed: {str(e)}"}
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def _generate_demo_people_data(self, query: str, limit: int) -> Dict:
101
  """Generate realistic demo data for people search"""
102
  demo_people = []
103
- job_titles = ["Software Engineer", "Marketing Manager", "Sales Director", "CEO", "CTO", "VP Sales", "Product Manager", "Data Scientist", "UX Designer", "DevOps Engineer"]
104
- companies = ["TechCorp", "InnovateLtd", "DataSystems", "CloudNine", "AIStartup", "FinTechPro", "HealthTech", "EduSolutions", "GreenTech", "CyberSec"]
105
- domains = ["techcorp.com", "innovate.ltd", "datasys.com", "cloudnine.io", "aistartup.ai", "fintech.pro", "healthtech.com", "edusol.com", "greentech.co", "cybersec.net"]
106
-
 
 
 
 
 
 
 
 
107
  first_names = ["John", "Jane", "Michael", "Sarah", "David", "Emily", "Robert", "Lisa", "James", "Maria"]
108
  last_names = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"]
109
 
@@ -141,8 +194,10 @@ class ApolloDataFetcher:
141
  def _generate_demo_company_data(self, query: str, limit: int) -> Dict:
142
  """Generate realistic demo data for company search"""
143
  demo_companies = []
144
- industries = ["Technology", "Healthcare", "Finance", "Education", "E-commerce", "Manufacturing", "Consulting", "Media", "Biotechnology", "Renewable Energy"]
145
-
 
 
146
  company_suffixes = ["Corp", "Inc", "LLC", "Ltd", "Technologies", "Solutions", "Systems", "Labs", "Ventures", "Group"]
147
 
148
  for i in range(min(limit, 10)):
@@ -207,40 +262,47 @@ class ApolloDataFetcher:
207
  }
208
  }
209
 
210
- # Initialize the Apollo client with environment variable
211
- apollo_client = ApolloDataFetcher()
 
212
 
213
  def format_people_results(results: Dict) -> tuple:
214
  """Format people search results for display"""
215
  if "error" in results:
216
  return f"❌ Error: {results['error']}", None
217
 
218
- if "people" not in results or not results["people"]:
 
 
219
  return "No results found.", None
220
 
221
  # Create formatted text output
222
- output_text = f"βœ… Found {len(results['people'])} people:\n\n"
223
 
224
  # Create DataFrame for table
225
  table_data = []
226
 
227
- for person in results["people"]:
 
 
 
228
  # Format text output
229
- output_text += f"πŸ‘€ **{person.get('name', 'N/A')}**\n"
 
230
  output_text += f" πŸ“§ Email: {person.get('email', 'N/A')}\n"
231
- output_text += f" πŸ“ž Phone: {person.get('phone', 'N/A')}\n"
232
  output_text += f" πŸ’Ό Title: {person.get('title', 'N/A')}\n"
233
- output_text += f" 🏒 Company: {person.get('organization', {}).get('name', 'N/A')}\n"
234
  output_text += f" πŸ“ Location: {person.get('city', 'N/A')}, {person.get('state', 'N/A')}\n"
235
  output_text += f" πŸ”— LinkedIn: {person.get('linkedin_url', 'N/A')}\n\n"
236
 
237
  # Add to table data
238
  table_data.append({
239
- "Name": person.get('name', 'N/A'),
240
  "Email": person.get('email', 'N/A'),
241
- "Phone": person.get('phone', 'N/A'),
242
  "Title": person.get('title', 'N/A'),
243
- "Company": person.get('organization', {}).get('name', 'N/A'),
244
  "Location": f"{person.get('city', 'N/A')}, {person.get('state', 'N/A')}"
245
  })
246
 
@@ -252,21 +314,23 @@ def format_company_results(results: Dict) -> tuple:
252
  if "error" in results:
253
  return f"❌ Error: {results['error']}", None
254
 
255
- if "organizations" not in results or not results["organizations"]:
 
 
256
  return "No results found.", None
257
 
258
  # Create formatted text output
259
- output_text = f"βœ… Found {len(results['organizations'])} companies:\n\n"
260
 
261
  # Create DataFrame for table
262
  table_data = []
263
 
264
- for company in results["organizations"]:
265
  # Format text output
266
  output_text += f"🏒 **{company.get('name', 'N/A')}**\n"
267
  output_text += f" 🌐 Website: {company.get('website_url', 'N/A')}\n"
268
- output_text += f" 🏭 Industry: {company.get('industry', 'N/A')}\n"
269
- output_text += f" πŸ‘₯ Employees: {company.get('employees_range', 'N/A')}\n"
270
  output_text += f" πŸ“ž Phone: {company.get('phone', 'N/A')}\n"
271
  output_text += f" πŸ“ Location: {company.get('city', 'N/A')}, {company.get('state', 'N/A')}\n"
272
  output_text += f" πŸ“… Founded: {company.get('founded_year', 'N/A')}\n\n"
@@ -275,8 +339,8 @@ def format_company_results(results: Dict) -> tuple:
275
  table_data.append({
276
  "Company": company.get('name', 'N/A'),
277
  "Website": company.get('website_url', 'N/A'),
278
- "Industry": company.get('industry', 'N/A'),
279
- "Employees": company.get('employees_range', 'N/A'),
280
  "Location": f"{company.get('city', 'N/A')}, {company.get('state', 'N/A')}",
281
  "Founded": str(company.get('founded_year', 'N/A'))
282
  })
@@ -284,6 +348,10 @@ def format_company_results(results: Dict) -> tuple:
284
  df = pd.DataFrame(table_data)
285
  return output_text, df
286
 
 
 
 
 
287
  def search_people_interface(query: str, limit: int):
288
  """Interface function for people search"""
289
  if not query.strip():
@@ -300,6 +368,29 @@ def search_companies_interface(query: str, limit: int):
300
  results = apollo_client.search_companies(query, limit)
301
  return format_company_results(results)
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  def enrich_person_interface(email: str):
304
  """Interface function for person enrichment"""
305
  if not email.strip() or "@" not in email:
@@ -310,17 +401,23 @@ def enrich_person_interface(email: str):
310
  if "error" in results:
311
  return f"❌ Error: {results['error']}"
312
 
313
- if "person" not in results:
 
 
314
  return "No person data found for this email."
315
 
316
- person = results["person"]
317
  output = f"πŸ” **Enriched Person Data**\n\n"
318
- output += f"πŸ‘€ **Name:** {person.get('name', 'N/A')}\n"
 
 
 
 
 
319
  output += f"πŸ“§ **Email:** {person.get('email', 'N/A')}\n"
320
- output += f"πŸ“ž **Phone:** {person.get('phone', 'N/A')}\n"
321
  output += f"πŸ’Ό **Title:** {person.get('title', 'N/A')}\n"
322
- output += f"🏒 **Company:** {person.get('organization', {}).get('name', 'N/A')}\n"
323
- output += f"🌐 **Company Website:** {person.get('organization', {}).get('website_url', 'N/A')}\n"
324
  output += f"πŸ“ **Location:** {person.get('city', 'N/A')}, {person.get('state', 'N/A')}, {person.get('country', 'N/A')}\n"
325
  output += f"πŸ”— **LinkedIn:** {person.get('linkedin_url', 'N/A')}\n"
326
  output += f"🐦 **Twitter:** {person.get('twitter_url', 'N/A')}\n"
@@ -332,11 +429,25 @@ def enrich_person_interface(email: str):
332
 
333
  return output
334
 
 
 
 
 
 
 
335
  # Check if API key is available
336
  api_key_status = "πŸ”‘ Live API Mode" if apollo_client.api_key and apollo_client.api_key != "demo" else "🎭 Demo Mode"
337
 
338
- # Create Gradio interface
 
 
 
339
  with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
340
  gr.HTML(f"""
341
  <div style="text-align: center; padding: 20px;">
342
  <h1>πŸš€ Apollo.io Data Fetcher</h1>
@@ -349,8 +460,16 @@ with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
349
  </div>
350
  """)
351
 
 
 
 
 
352
  with gr.Tabs():
353
- # People Search Tab
 
 
 
 
354
  with gr.TabItem("πŸ‘₯ Search People"):
355
  with gr.Row():
356
  with gr.Column(scale=2):
@@ -382,7 +501,10 @@ with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
382
  outputs=[people_output, people_table]
383
  )
384
 
385
- # Companies Search Tab
 
 
 
386
  with gr.TabItem("🏒 Search Companies"):
387
  with gr.Row():
388
  with gr.Column(scale=2):
@@ -414,7 +536,46 @@ with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
414
  outputs=[company_output, company_table]
415
  )
416
 
417
- # Person Enrichment Tab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  with gr.TabItem("πŸ” Enrich Person"):
419
  email_input = gr.Textbox(
420
  label="Email Address",
@@ -431,8 +592,11 @@ with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
431
  outputs=[enrich_output]
432
  )
433
 
434
- # Setup Instructions Tab
435
- with gr.TabItem("βš™οΈ Setup Instructions"):
 
 
 
436
  gr.Markdown("""
437
  ## πŸ”§ How to Set Up Apollo.io API on Hugging Face
438
 
@@ -456,57 +620,115 @@ with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
456
  - If API key is configured correctly, you'll see "πŸ”‘ Live API Mode" at the top
457
  - If not configured, you'll see "🎭 Demo Mode" with sample data
458
 
459
- ### 🌟 Apollo.io API Features
 
 
 
 
 
 
 
 
 
460
 
461
- #### πŸ“Š Database Size
462
- - **275+ million contacts** (people)
463
- - **70+ million companies**
464
- - Global coverage with detailed B2B information
465
 
466
- #### πŸ› οΈ Available Features in This App
467
- 1. **People Search** - Find contacts by various criteria
468
- 2. **Company Search** - Discover businesses and organizations
469
- 3. **Contact Enrichment** - Add details to existing contacts
470
 
471
- #### πŸ’‘ Use Cases
472
- - **Sales Automation**: Find and contact prospects
473
- - **Lead Generation**: Build targeted contact lists
474
- - **CRM Integration**: Sync with Salesforce, HubSpot
475
- - **Data Enrichment**: Complete incomplete contact records
476
- - **Market Research**: Analyze companies and industries
477
 
478
- #### ⚑ Rate Limits
479
- - Free plan: 50 requests/month
480
- - Paid plans: Higher limits based on subscription
481
- - Enterprise: Custom rate limits available
 
482
 
483
- #### πŸ”’ Security Features
484
  - API keys with granular permissions
485
  - HTTPS encryption for all requests
486
  - SOC 2 Type II compliant
 
487
 
488
- ---
 
 
 
 
489
 
490
- ### 🚨 Troubleshooting
 
 
 
 
491
 
492
- #### Common Issues:
493
- 1. **"Invalid API key" error**: Check if APOLLO_API_KEY secret is set correctly
494
- 2. **"Rate limit exceeded"**: You've hit your plan's limit, wait or upgrade
495
- 3. **No results found**: Try broader search terms or check your query
496
- 4. **Demo mode active**: API key not configured in repository secrets
497
 
498
- #### Need Help?
499
- - Check [Apollo.io API Documentation](https://apolloio.github.io/apollo-api-docs/)
500
- - Contact Apollo.io support for API issues
501
- - Check Hugging Face documentation for Spaces setup
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  """)
503
 
 
 
 
 
504
  gr.HTML("""
505
  <div style="text-align: center; padding: 20px; color: #666;">
506
- <p>πŸš€ Built with Apollo.io API & Gradio | <a href="https://apolloio.github.io/apollo-api-docs/" target="_blank">API Docs</a> | <a href="https://huggingface.co/docs/hub/spaces-overview" target="_blank">HF Spaces</a></p>
 
 
507
  </div>
508
  """)
509
 
510
- # Launch the app
 
 
 
511
  if __name__ == "__main__":
512
  demo.launch()
 
 
1
  import gradio as gr
2
  import requests
3
  import json
 
7
  import time
8
  import random
9
 
10
+ # ==========================================
11
+ # APOLLO.IO API CLIENT CLASS
12
+ # ==========================================
13
+
14
  class ApolloDataFetcher:
15
+ """
16
+ Apollo.io API client for fetching people and company data
17
+ """
18
+
19
  def __init__(self, api_key: str = None):
20
+ # Get API key from environment (Hugging Face secrets) or parameter
21
  self.api_key = api_key or os.getenv("APOLLO_API_KEY")
22
  self.base_url = "https://api.apollo.io/v1"
23
  self.headers = {
 
27
  if self.api_key and self.api_key != "demo":
28
  self.headers["X-Api-Key"] = self.api_key
29
 
30
+ # ==========================================
31
+ # PEOPLE SEARCH METHOD
32
+ # ==========================================
33
+
34
  def search_people(self, query: str, limit: int = 10) -> Dict:
35
  """Search for people using Apollo.io API"""
36
  if not self.api_key or self.api_key == "demo":
37
  return self._generate_demo_people_data(query, limit)
38
 
39
+ endpoint = f"{self.base_url}/people/search"
40
  payload = {
41
  "q_keywords": query,
42
  "page": 1,
43
+ "per_page": min(limit, 25),
44
+ "person_locations": ["United States"],
45
  }
46
 
47
  try:
48
  response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
49
+ return self._handle_api_response(response)
 
 
 
 
 
 
 
50
  except requests.exceptions.Timeout:
51
  return {"error": "Request timeout. Please try again."}
52
  except Exception as e:
53
  return {"error": f"Request failed: {str(e)}"}
54
 
55
+ # ==========================================
56
+ # COMPANY SEARCH METHOD
57
+ # ==========================================
58
+
59
  def search_companies(self, query: str, limit: int = 10) -> Dict:
60
  """Search for companies using Apollo.io API"""
61
  if not self.api_key or self.api_key == "demo":
62
  return self._generate_demo_company_data(query, limit)
63
 
64
+ endpoint = f"{self.base_url}/organizations/search"
65
  payload = {
66
  "q_keywords": query,
67
  "page": 1,
68
+ "per_page": min(limit, 25),
69
+ "organization_locations": ["United States"],
70
  }
71
 
72
  try:
73
  response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
74
+ return self._handle_api_response(response)
 
 
 
 
 
 
 
75
  except requests.exceptions.Timeout:
76
  return {"error": "Request timeout. Please try again."}
77
  except Exception as e:
78
  return {"error": f"Request failed: {str(e)}"}
79
 
80
+ # ==========================================
81
+ # CONTACT ENRICHMENT METHOD
82
+ # ==========================================
83
+
84
  def enrich_person(self, email: str) -> Dict:
85
  """Enrich person data by email"""
86
  if not self.api_key or self.api_key == "demo":
 
91
 
92
  try:
93
  response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
94
+ return self._handle_api_response(response)
95
+ except requests.exceptions.Timeout:
96
+ return {"error": "Request timeout. Please try again."}
97
+ except Exception as e:
98
+ return {"error": f"Request failed: {str(e)}"}
99
+
100
+ # ==========================================
101
+ # EMAIL FINDER METHOD
102
+ # ==========================================
103
+
104
+ def find_email(self, first_name: str, last_name: str, domain: str) -> Dict:
105
+ """Find email for a person"""
106
+ if not self.api_key or self.api_key == "demo":
107
+ return {"email": f"{first_name.lower()}.{last_name.lower()}@{domain}"}
108
+
109
+ endpoint = f"{self.base_url}/email_accounts"
110
+ payload = {
111
+ "first_name": first_name,
112
+ "last_name": last_name,
113
+ "domain": domain
114
+ }
115
+
116
+ try:
117
+ response = requests.post(endpoint, headers=self.headers, json=payload, timeout=10)
118
+ return self._handle_api_response(response)
119
  except requests.exceptions.Timeout:
120
  return {"error": "Request timeout. Please try again."}
121
  except Exception as e:
122
  return {"error": f"Request failed: {str(e)}"}
123
 
124
+ # ==========================================
125
+ # API RESPONSE HANDLER
126
+ # ==========================================
127
+
128
+ def _handle_api_response(self, response) -> Dict:
129
+ """Handle API response with proper error codes"""
130
+ if response.status_code == 200:
131
+ return response.json()
132
+ elif response.status_code == 401:
133
+ return {"error": "Invalid API key. Please check your Apollo.io API key."}
134
+ elif response.status_code == 403:
135
+ return {"error": "Access denied. Your API key may not have permission for this endpoint. Try upgrading your Apollo.io plan."}
136
+ elif response.status_code == 429:
137
+ return {"error": "Rate limit exceeded. Please try again later."}
138
+ else:
139
+ return {"error": f"API Error: {response.status_code} - {response.text}"}
140
+
141
+ # ==========================================
142
+ # DEMO DATA GENERATORS
143
+ # ==========================================
144
+
145
  def _generate_demo_people_data(self, query: str, limit: int) -> Dict:
146
  """Generate realistic demo data for people search"""
147
  demo_people = []
148
+ job_titles = [
149
+ "Software Engineer", "Marketing Manager", "Sales Director", "CEO", "CTO",
150
+ "VP Sales", "Product Manager", "Data Scientist", "UX Designer", "DevOps Engineer"
151
+ ]
152
+ companies = [
153
+ "TechCorp", "InnovateLtd", "DataSystems", "CloudNine", "AIStartup",
154
+ "FinTechPro", "HealthTech", "EduSolutions", "GreenTech", "CyberSec"
155
+ ]
156
+ domains = [
157
+ "techcorp.com", "innovate.ltd", "datasys.com", "cloudnine.io", "aistartup.ai",
158
+ "fintech.pro", "healthtech.com", "edusol.com", "greentech.co", "cybersec.net"
159
+ ]
160
  first_names = ["John", "Jane", "Michael", "Sarah", "David", "Emily", "Robert", "Lisa", "James", "Maria"]
161
  last_names = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"]
162
 
 
194
  def _generate_demo_company_data(self, query: str, limit: int) -> Dict:
195
  """Generate realistic demo data for company search"""
196
  demo_companies = []
197
+ industries = [
198
+ "Technology", "Healthcare", "Finance", "Education", "E-commerce",
199
+ "Manufacturing", "Consulting", "Media", "Biotechnology", "Renewable Energy"
200
+ ]
201
  company_suffixes = ["Corp", "Inc", "LLC", "Ltd", "Technologies", "Solutions", "Systems", "Labs", "Ventures", "Group"]
202
 
203
  for i in range(min(limit, 10)):
 
262
  }
263
  }
264
 
265
+ # ==========================================
266
+ # DATA FORMATTING FUNCTIONS
267
+ # ==========================================
268
 
269
  def format_people_results(results: Dict) -> tuple:
270
  """Format people search results for display"""
271
  if "error" in results:
272
  return f"❌ Error: {results['error']}", None
273
 
274
+ # Handle both 'people' and 'data' keys (different API versions)
275
+ people_data = results.get("people", results.get("data", []))
276
+ if not people_data:
277
  return "No results found.", None
278
 
279
  # Create formatted text output
280
+ output_text = f"βœ… Found {len(people_data)} people:\n\n"
281
 
282
  # Create DataFrame for table
283
  table_data = []
284
 
285
+ for person in people_data:
286
+ # Handle nested organization data
287
+ org = person.get('organization', {}) or person.get('current_organization', {})
288
+
289
  # Format text output
290
+ name = person.get('name', f"{person.get('first_name', '')} {person.get('last_name', '')}").strip()
291
+ output_text += f"πŸ‘€ **{name or 'N/A'}**\n"
292
  output_text += f" πŸ“§ Email: {person.get('email', 'N/A')}\n"
293
+ output_text += f" πŸ“ž Phone: {person.get('phone', person.get('personal_phone', 'N/A'))}\n"
294
  output_text += f" πŸ’Ό Title: {person.get('title', 'N/A')}\n"
295
+ output_text += f" 🏒 Company: {org.get('name', 'N/A')}\n"
296
  output_text += f" πŸ“ Location: {person.get('city', 'N/A')}, {person.get('state', 'N/A')}\n"
297
  output_text += f" πŸ”— LinkedIn: {person.get('linkedin_url', 'N/A')}\n\n"
298
 
299
  # Add to table data
300
  table_data.append({
301
+ "Name": name or 'N/A',
302
  "Email": person.get('email', 'N/A'),
303
+ "Phone": person.get('phone', person.get('personal_phone', 'N/A')),
304
  "Title": person.get('title', 'N/A'),
305
+ "Company": org.get('name', 'N/A'),
306
  "Location": f"{person.get('city', 'N/A')}, {person.get('state', 'N/A')}"
307
  })
308
 
 
314
  if "error" in results:
315
  return f"❌ Error: {results['error']}", None
316
 
317
+ # Handle both 'organizations' and 'data' keys
318
+ companies_data = results.get("organizations", results.get("data", []))
319
+ if not companies_data:
320
  return "No results found.", None
321
 
322
  # Create formatted text output
323
+ output_text = f"βœ… Found {len(companies_data)} companies:\n\n"
324
 
325
  # Create DataFrame for table
326
  table_data = []
327
 
328
+ for company in companies_data:
329
  # Format text output
330
  output_text += f"🏒 **{company.get('name', 'N/A')}**\n"
331
  output_text += f" 🌐 Website: {company.get('website_url', 'N/A')}\n"
332
+ output_text += f" 🏭 Industry: {company.get('industry', company.get('primary_industry', 'N/A'))}\n"
333
+ output_text += f" πŸ‘₯ Employees: {company.get('employees_range', company.get('estimated_num_employees', 'N/A'))}\n"
334
  output_text += f" πŸ“ž Phone: {company.get('phone', 'N/A')}\n"
335
  output_text += f" πŸ“ Location: {company.get('city', 'N/A')}, {company.get('state', 'N/A')}\n"
336
  output_text += f" πŸ“… Founded: {company.get('founded_year', 'N/A')}\n\n"
 
339
  table_data.append({
340
  "Company": company.get('name', 'N/A'),
341
  "Website": company.get('website_url', 'N/A'),
342
+ "Industry": company.get('industry', company.get('primary_industry', 'N/A')),
343
+ "Employees": str(company.get('employees_range', company.get('estimated_num_employees', 'N/A'))),
344
  "Location": f"{company.get('city', 'N/A')}, {company.get('state', 'N/A')}",
345
  "Founded": str(company.get('founded_year', 'N/A'))
346
  })
 
348
  df = pd.DataFrame(table_data)
349
  return output_text, df
350
 
351
+ # ==========================================
352
+ # INTERFACE FUNCTIONS
353
+ # ==========================================
354
+
355
  def search_people_interface(query: str, limit: int):
356
  """Interface function for people search"""
357
  if not query.strip():
 
368
  results = apollo_client.search_companies(query, limit)
369
  return format_company_results(results)
370
 
371
+ def find_email_interface(first_name: str, last_name: str, domain: str):
372
+ """Interface function for email finding"""
373
+ if not all([first_name.strip(), last_name.strip(), domain.strip()]):
374
+ return "Please enter first name, last name, and company domain."
375
+
376
+ results = apollo_client.find_email(first_name, last_name, domain)
377
+
378
+ if "error" in results:
379
+ return f"❌ Error: {results['error']}"
380
+
381
+ if "email" in results:
382
+ output = f"πŸ“§ **Email Found**\n\n"
383
+ output += f"πŸ‘€ **Name:** {first_name} {last_name}\n"
384
+ output += f"πŸ“§ **Email:** {results['email']}\n"
385
+ output += f"🏒 **Domain:** {domain}\n"
386
+
387
+ if results.get('confidence'):
388
+ output += f"🎯 **Confidence:** {results['confidence']}\n"
389
+
390
+ return output
391
+
392
+ return "No email found for this person and domain combination."
393
+
394
  def enrich_person_interface(email: str):
395
  """Interface function for person enrichment"""
396
  if not email.strip() or "@" not in email:
 
401
  if "error" in results:
402
  return f"❌ Error: {results['error']}"
403
 
404
+ # Handle both 'person' and 'data' keys
405
+ person = results.get("person", results.get("data"))
406
+ if not person:
407
  return "No person data found for this email."
408
 
 
409
  output = f"πŸ” **Enriched Person Data**\n\n"
410
+
411
+ # Handle nested organization data
412
+ org = person.get('organization', {}) or person.get('current_organization', {})
413
+ name = person.get('name', f"{person.get('first_name', '')} {person.get('last_name', '')}").strip()
414
+
415
+ output += f"πŸ‘€ **Name:** {name or 'N/A'}\n"
416
  output += f"πŸ“§ **Email:** {person.get('email', 'N/A')}\n"
417
+ output += f"πŸ“ž **Phone:** {person.get('phone', person.get('personal_phone', 'N/A'))}\n"
418
  output += f"πŸ’Ό **Title:** {person.get('title', 'N/A')}\n"
419
+ output += f"🏒 **Company:** {org.get('name', 'N/A')}\n"
420
+ output += f"🌐 **Company Website:** {org.get('website_url', 'N/A')}\n"
421
  output += f"πŸ“ **Location:** {person.get('city', 'N/A')}, {person.get('state', 'N/A')}, {person.get('country', 'N/A')}\n"
422
  output += f"πŸ”— **LinkedIn:** {person.get('linkedin_url', 'N/A')}\n"
423
  output += f"🐦 **Twitter:** {person.get('twitter_url', 'N/A')}\n"
 
429
 
430
  return output
431
 
432
+ # ==========================================
433
+ # INITIALIZE APOLLO CLIENT
434
+ # ==========================================
435
+
436
+ apollo_client = ApolloDataFetcher()
437
+
438
  # Check if API key is available
439
  api_key_status = "πŸ”‘ Live API Mode" if apollo_client.api_key and apollo_client.api_key != "demo" else "🎭 Demo Mode"
440
 
441
+ # ==========================================
442
+ # GRADIO INTERFACE
443
+ # ==========================================
444
+
445
  with gr.Blocks(title="Apollo.io Data Fetcher", theme=gr.themes.Soft()) as demo:
446
+
447
+ # ==========================================
448
+ # HEADER SECTION
449
+ # ==========================================
450
+
451
  gr.HTML(f"""
452
  <div style="text-align: center; padding: 20px;">
453
  <h1>πŸš€ Apollo.io Data Fetcher</h1>
 
460
  </div>
461
  """)
462
 
463
+ # ==========================================
464
+ # MAIN TABS
465
+ # ==========================================
466
+
467
  with gr.Tabs():
468
+
469
+ # ==========================================
470
+ # PEOPLE SEARCH TAB
471
+ # ==========================================
472
+
473
  with gr.TabItem("πŸ‘₯ Search People"):
474
  with gr.Row():
475
  with gr.Column(scale=2):
 
501
  outputs=[people_output, people_table]
502
  )
503
 
504
+ # ==========================================
505
+ # COMPANIES SEARCH TAB
506
+ # ==========================================
507
+
508
  with gr.TabItem("🏒 Search Companies"):
509
  with gr.Row():
510
  with gr.Column(scale=2):
 
536
  outputs=[company_output, company_table]
537
  )
538
 
539
+ # ==========================================
540
+ # EMAIL FINDER TAB
541
+ # ==========================================
542
+
543
+ with gr.TabItem("πŸ“§ Find Email"):
544
+ gr.Markdown("### Find email addresses for specific people")
545
+
546
+ with gr.Row():
547
+ with gr.Column():
548
+ first_name_input = gr.Textbox(
549
+ label="First Name",
550
+ placeholder="e.g., John",
551
+ info="Person's first name"
552
+ )
553
+ with gr.Column():
554
+ last_name_input = gr.Textbox(
555
+ label="Last Name",
556
+ placeholder="e.g., Smith",
557
+ info="Person's last name"
558
+ )
559
+ with gr.Column():
560
+ domain_input = gr.Textbox(
561
+ label="Company Domain",
562
+ placeholder="e.g., apple.com",
563
+ info="Company website domain (without https://)"
564
+ )
565
+
566
+ find_email_btn = gr.Button("πŸ“§ Find Email", variant="primary", size="lg")
567
+ email_finder_output = gr.Markdown(label="Email Results")
568
+
569
+ find_email_btn.click(
570
+ find_email_interface,
571
+ inputs=[first_name_input, last_name_input, domain_input],
572
+ outputs=[email_finder_output]
573
+ )
574
+
575
+ # ==========================================
576
+ # PERSON ENRICHMENT TAB
577
+ # ==========================================
578
+
579
  with gr.TabItem("πŸ” Enrich Person"):
580
  email_input = gr.Textbox(
581
  label="Email Address",
 
592
  outputs=[enrich_output]
593
  )
594
 
595
+ # ==========================================
596
+ # SETUP INSTRUCTIONS TAB
597
+ # ==========================================
598
+
599
+ with gr.TabItem("βš™οΈ Setup & Troubleshooting"):
600
  gr.Markdown("""
601
  ## πŸ”§ How to Set Up Apollo.io API on Hugging Face
602
 
 
620
  - If API key is configured correctly, you'll see "πŸ”‘ Live API Mode" at the top
621
  - If not configured, you'll see "🎭 Demo Mode" with sample data
622
 
623
+ ## 🚨 Common API Access Issues & Solutions
624
+
625
+ ### 403 Error - Access Denied
626
+ **Problem**: Your API key doesn't have access to certain endpoints.
627
+
628
+ **Solutions**:
629
+ 1. **Try Email Finder**: Often more accessible than people/company search
630
+ 2. **Check API Plan**: Some features require paid Apollo.io plans
631
+ 3. **Verify Permissions**: Ensure your API key has the right permissions
632
+ 4. **Use Contact Enrichment**: Usually available on all plans
633
 
634
+ ### Available Features by Plan:
 
 
 
635
 
636
+ #### πŸ†“ Free Plan
637
+ - Contact enrichment (limited)
638
+ - Email finder (limited)
639
+ - Basic API access
640
 
641
+ #### πŸ’° Paid Plans
642
+ - Full people search
643
+ - Company search
644
+ - Unlimited contact enrichment
645
+ - Higher rate limits
 
646
 
647
+ ### Rate Limits
648
+ - **Free**: 50 requests/month
649
+ - **Starter**: 1,000+ requests/month
650
+ - **Professional**: 5,000+ requests/month
651
+ - **Organization**: Custom limits
652
 
653
+ ## πŸ”’ Security Features
654
  - API keys with granular permissions
655
  - HTTPS encryption for all requests
656
  - SOC 2 Type II compliant
657
+ - Secure secret management via Hugging Face
658
 
659
+ ## πŸ“Š Apollo.io Database Stats
660
+ - **275+ million contacts** worldwide
661
+ - **70+ million companies** across industries
662
+ - Global B2B coverage with detailed information
663
+ - Regularly updated and verified data
664
 
665
+ ## πŸ› οΈ Technical Details
666
+ - Built with Gradio for interactive web interface
667
+ - Uses Apollo.io REST API for data retrieval
668
+ - Pandas for data processing and table display
669
+ - Error handling and rate limiting awareness
670
 
671
+ ## 🚨 Troubleshooting Guide
 
 
 
 
672
 
673
+ #### Issue: "API Error: 403 - Access Denied"
674
+ **Solution**: Your API key lacks permissions. Try:
675
+ - Upgrading your Apollo.io plan
676
+ - Using Email Finder instead of People Search
677
+ - Checking API key permissions in Apollo.io
678
+
679
+ #### Issue: "Invalid API key"
680
+ **Solution**:
681
+ - Check if APOLLO_API_KEY secret is set correctly in Space settings
682
+ - Verify the API key is copied correctly from Apollo.io
683
+ - Restart your Space after adding the secret
684
+
685
+ #### Issue: "Rate limit exceeded"
686
+ **Solution**:
687
+ - Wait for the rate limit to reset
688
+ - Upgrade your Apollo.io plan for higher limits
689
+ - Use demo mode for testing
690
+
691
+ #### Issue: "No results found"
692
+ **Solution**:
693
+ - Try broader search terms
694
+ - Check spelling and formatting
695
+ - Use different keywords or filters
696
+
697
+ #### Issue: Demo mode always active
698
+ **Solution**:
699
+ - Ensure APOLLO_API_KEY is added to repository secrets
700
+ - Check the secret name is exactly: `APOLLO_API_KEY`
701
+ - Restart the Space after adding secrets
702
+
703
+ ## πŸ’‘ Best Practices
704
+
705
+ 1. **Start with Demo Mode**: Test the interface before adding API key
706
+ 2. **Monitor Usage**: Check Apollo.io dashboard for API usage
707
+ 3. **Use Specific Searches**: More specific queries return better results
708
+ 4. **Export Data**: Use table view to copy/export results
709
+ 5. **Respect Rate Limits**: Don't exceed your plan's limits
710
+
711
+ ## πŸ“ž Support Resources
712
+ - **Apollo.io API Docs**: [apolloio.github.io/apollo-api-docs](https://apolloio.github.io/apollo-api-docs/)
713
+ - **Apollo.io Support**: Contact through their platform
714
+ - **Hugging Face Docs**: [huggingface.co/docs/hub/spaces-overview](https://huggingface.co/docs/hub/spaces-overview)
715
  """)
716
 
717
+ # ==========================================
718
+ # FOOTER
719
+ # ==========================================
720
+
721
  gr.HTML("""
722
  <div style="text-align: center; padding: 20px; color: #666;">
723
+ <p>πŸš€ Built with Apollo.io API & Gradio |
724
+ <a href="https://apolloio.github.io/apollo-api-docs/" target="_blank">API Docs</a> |
725
+ <a href="https://huggingface.co/docs/hub/spaces-overview" target="_blank">HF Spaces</a></p>
726
  </div>
727
  """)
728
 
729
+ # ==========================================
730
+ # LAUNCH APPLICATION
731
+ # ==========================================
732
+
733
  if __name__ == "__main__":
734
  demo.launch()