
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
nlpearl
Advanced tools
NLPearl is a Python wrapper for the NLPearl API, allowing developers to interact seamlessly with NLPearl's services. This package supports both API V1 and V2 with a unified interface.
pip install nlpearl
import nlpearl as pearl
# Configuration
pearl.api_key = "your_api_key_here"
pearl.api_version = "v2" # Default, can be "v1" or "v2"
import nlpearl as pearl
pearl.api_key = "your_key"
pearl.api_version = "v2" # Default
# Get all pearls
pearls = pearl.Pearl.get_all()
# Add a lead using pearl_id
pearl.Outbound.add_lead(
pearl_id,
phone_number="+1234567890",
call_data={"firstName": "John"}
)
# Get analytics
analytics = pearl.Pearl.get_analytics(
pearl_id,
from_date="2024-01-01T00:00:00.000Z",
to_date="2024-01-31T23:59:59.999Z"
)
import nlpearl as pearl
pearl.api_key = "your_key"
pearl.api_version = "v1"
# Get all inbounds
inbounds = pearl.Inbound.get_all()
# Add a lead using outbound_id
pearl.Outbound.add_lead(
outbound_id,
phone_number="+1234567890",
call_data={"firstName": "John"}
)
# Get analytics
analytics = pearl.Outbound.get_analytics(
outbound_id,
from_date="2024-01-01T00:00:00.000Z",
to_date="2024-01-31T23:59:59.999Z"
)
The wrapper uses the same class names for both V1 and V2, automatically routing to the correct endpoints based on pearl.api_version.
| Feature | V1 | V2 |
|---|---|---|
| Default | No | Yes |
| Structure | Separate Inbound/Outbound | Unified Pearl |
| ID Parameter | outbound_id, inbound_id | pearl_id |
| Classes | Inbound, Outbound | Pearl |
| Lead Operations | Outbound.add_lead(outbound_id, ...) | Outbound.add_lead(pearl_id, ...) |
✅ Same class names - Use pearl.Outbound, pearl.Pearl, pearl.Inbound
✅ Automatic routing - Methods route to correct version based on api_version
✅ Clear errors - Helpful messages when using wrong version
✅ Version switching - Change api_version anytime
No separate V2 classes needed! Just set the version once and use the same API.
pearl.api_version = "v2"
# Get all pearls
pearls = pearl.Pearl.get_all()
# Get specific pearl
pearl_details = pearl.Pearl.get(pearl_id)
# Activate/Deactivate
pearl.Pearl.set_active(pearl_id, is_active=True)
# Get ongoing calls
ongoing = pearl.Pearl.get_ongoing_calls(pearl_id)
# Get calls with filters
calls = pearl.Pearl.get_calls(
pearl_id,
from_date=datetime(2024, 1, 1),
to_date=datetime(2024, 1, 31),
tags=["important"],
statuses=[100, 130]
)
# Get analytics
analytics = pearl.Pearl.get_analytics(pearl_id, from_date, to_date)
# Add lead (using pearl_id)
lead = pearl.Outbound.add_lead(
pearl_id,
phone_number="+1234567890",
external_id="ext123",
time_zone_id="Pacific Standard Time",
call_data={"firstName": "John", "lastName": "Doe"}
)
# Get lead
lead = pearl.Outbound.get_lead_by_id(pearl_id, lead_id)
lead = pearl.Outbound.get_lead_by_external_id(pearl_id, "ext123")
lead = pearl.Outbound.get_lead_by_phone_number(pearl_id, "+1234567890")
# Update lead
pearl.Outbound.update_lead(
pearl_id,
lead_id,
status=100, # Mark as Success
call_data={"notes": "Contact successful"}
)
# Search leads
leads = pearl.Outbound.get_leads(
pearl_id,
statuses=[1, 10], # New and NeedRetry
search_input="John",
limit=50
)
# Delete leads
pearl.Outbound.delete_leads(pearl_id, [lead_id1, lead_id2])
pearl.Outbound.delete_leads_by_external_id(pearl_id, ["ext1", "ext2"])
pearl.api_version = "v1"
# Get all inbounds
inbounds = pearl.Inbound.get_all()
# Get specific inbound
inbound = pearl.Inbound.get(inbound_id)
# Activate/Deactivate
pearl.Inbound.set_active(inbound_id, is_active=True)
# Get ongoing calls
ongoing = pearl.Inbound.get_ongoing_calls(inbound_id)
# Get calls
calls = pearl.Inbound.get_calls(
inbound_id,
from_date=datetime(2024, 1, 1),
to_date=datetime(2024, 1, 31),
tags=["important"]
)
# Get analytics
analytics = pearl.Inbound.get_analytics(inbound_id, from_date, to_date)
# Get all outbounds
outbounds = pearl.Outbound.get_all()
# Get specific outbound
outbound = pearl.Outbound.get(outbound_id)
# Activate/Deactivate
pearl.Outbound.set_active(outbound_id, is_active=True)
# Get calls
calls = pearl.Outbound.get_calls(
outbound_id,
from_date=datetime(2024, 1, 1),
to_date=datetime(2024, 1, 31)
)
# Make a call
result = pearl.Outbound.make_call(
outbound_id,
to="+1234567890",
call_data={"firstName": "Jane"}
)
# Get call requests
requests = pearl.Outbound.get_call_requests(
outbound_id,
from_date=datetime(2024, 1, 1),
to_date=datetime(2024, 1, 31)
)
# Get analytics
analytics = pearl.Outbound.get_analytics(outbound_id, from_date, to_date)
# Add lead (using outbound_id)
lead = pearl.Outbound.add_lead(
outbound_id, # Note: outbound_id in V1, pearl_id in V2
phone_number="+1234567890",
external_id="ext123",
call_data={"firstName": "John"}
)
# Update lead
pearl.Outbound.update_lead(outbound_id, lead_id, status=100)
# Search leads
leads = pearl.Outbound.get_leads(
outbound_id,
status=1 # Note: V1 uses 'status', V2 uses 'statuses'
)
# Other lead methods work the same
lead = pearl.Outbound.get_lead_by_id(outbound_id, lead_id)
pearl.Outbound.delete_leads(outbound_id, [lead_id1, lead_id2])
These methods work in both V1 and V2:
# Works in both versions
account = pearl.Account.get_account()
print(f"Balance: {account['creditBalance']}")
# Get call info (both versions)
call = pearl.Call.get_call(call_id)
# Delete calls (both versions)
pearl.Call.delete_calls([call_id1, call_id2])
# V1
pearl.Pearl.reset_customer_memory(pearl_id, phone_number)
# V2 (same method works)
pearl.Pearl.reset_memory(pearl_id, phone_number)
| Class | Method | V1 | V2 | Notes |
|---|---|---|---|---|
| Account | get_account() | ✅ | ✅ | |
| Call | get_call(call_id) | ✅ | ✅ | |
| Call | delete_calls(call_ids) | ✅ | ✅ | |
| Inbound | get_all() | ✅ | ❌ | Use Pearl.get_all() in V2 |
| Inbound | get(inbound_id) | ✅ | ❌ | Use Pearl.get(pearl_id) in V2 |
| Inbound | set_active(...) | ✅ | ❌ | Use Pearl.set_active(...) in V2 |
| Inbound | get_calls(...) | ✅ | ❌ | Use Pearl.get_calls(...) in V2 |
| Inbound | get_ongoing_calls(...) | ✅ | ❌ | Use Pearl.get_ongoing_calls(...) in V2 |
| Inbound | get_analytics(...) | ✅ | ❌ | Use Pearl.get_analytics(...) in V2 |
| Outbound | get_all() | ✅ | ❌ | Use Pearl.get_all() in V2 |
| Outbound | get(outbound_id) | ✅ | ❌ | Use Pearl.get(pearl_id) in V2 |
| Outbound | set_active(...) | ✅ | ❌ | Use Pearl.set_active(...) in V2 |
| Outbound | get_calls(...) | ✅ | ❌ | Use Pearl.get_calls(...) in V2 |
| Outbound | add_lead(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | update_lead(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | get_leads(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | get_lead_by_id(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | get_lead_by_external_id(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | get_lead_by_phone_number(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | delete_leads(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | delete_leads_by_external_id(id, ...) | ✅ | ✅ | V1: outbound_id, V2: pearl_id |
| Outbound | make_call(...) | ✅ | ❌ | V1 only |
| Outbound | get_call_request(...) | ✅ | ❌ | V1 only |
| Outbound | get_call_requests(...) | ✅ | ❌ | V1 only |
| Outbound | get_analytics(...) | ✅ | ❌ | Use Pearl.get_analytics(...) in V2 |
| Pearl | reset_customer_memory(...) | ✅ | ✅ | |
| Pearl | reset_memory(...) | ✅ | ✅ | Same as above |
| Pearl | get_all() | ❌ | ✅ | V2 only |
| Pearl | get(pearl_id) | ❌ | ✅ | V2 only |
| Pearl | set_active(...) | ❌ | ✅ | V2 only |
| Pearl | get_calls(...) | ❌ | ✅ | V2 only |
| Pearl | get_ongoing_calls(...) | ❌ | ✅ | V2 only |
| Pearl | get_analytics(...) | ❌ | ✅ | V2 only |
1 - New10 - NeedRetry100 - Success110 - NotSuccessful130 - Completed150 - Unreachable220 - Blacklisted500 - Error3 - InProgress4 - Completed5 - Busy6 - Failed7 - NoAnswer8 - Canceled1 - Running2 - Paused3 - Suspended10 - TemporaryMaintenanceBefore (V1):
pearl.api_version = "v1"
# Get outbounds
outbounds = pearl.Outbound.get_all()
# Add lead
pearl.Outbound.add_lead(outbound_id, phone_number="+123...")
# Get analytics
analytics = pearl.Outbound.get_analytics(outbound_id, from_date, to_date)
After (V2):
pearl.api_version = "v2"
# Get pearls (replaces outbounds/inbounds)
pearls = pearl.Pearl.get_all()
# Add lead (same method, different ID)
pearl.Outbound.add_lead(pearl_id, phone_number="+123...")
# Get analytics (use Pearl class)
analytics = pearl.Pearl.get_analytics(pearl_id, from_date, to_date)
outbound_id/inbound_id with pearl_idPearl class for analytics, calls, and pearl managementOutbound for lead operations (just change the ID)Inbound class is V1-only, use Pearl in V2The wrapper provides clear error messages when using methods in the wrong version:
pearl.api_version = "v2"
pearl.Inbound.get_all()
# ValueError: Inbound.get_all() is only available in API v1.
# In v2, use Pearl.get_all() instead.
BSD 3-Clause License - see LICENSE file for details.
Version: 2.0.0
Python: 3.6+
API Versions: V1 and V2 supported
FAQs
A Python wrapper for the NLPearl API
We found that nlpearl demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.