ninjapear
NinjaPear API Integration Skill
Your job is to WRITE CODE that calls the NinjaPear API, not to query it yourself. If the user asks you to "get customers of stripe.com using NinjaPear", write a Python or JavaScript script that they can run -- do not use MCP tools.
When to Use This Skill
Use this skill when the user asks you to write code that:
- Calls any NinjaPear API endpoint (nubela.co/api/v1/*)
- Gets company data (details, executives, employees, funding, updates) from NinjaPear
- Gets customers, investors, or partners of a company using NinjaPear
- Gets competitors of a company using NinjaPear
- Looks up person/employee profiles using NinjaPear
- Validates emails (disposable/free) using NinjaPear
- Sets up company monitoring feeds using NinjaPear
- Integrates NinjaPear into their application or pipeline
Setup: API Key
- The user needs an API key from https://nubela.co/dashboard
- Store it as the
NINJAPEAR_API_KEYenvironment variable - NEVER hardcode the API key in source code -- always read from environment
# .env
NINJAPEAR_API_KEY=your_api_key_here
To verify the key works, call the free credit balance endpoint:
curl -H "Authorization: Bearer $NINJAPEAR_API_KEY" https://nubela.co/api/v1/meta/credit-balance
If the project has a .env.example, add NINJAPEAR_API_KEY= there too.
Use the Official SDKs
Prefer the official SDKs over raw HTTP. Fall back to direct HTTP calls only for languages without an SDK.
Python
pip install ninjapear
# or: uv add ninjapear
import os
import ninjapear
config = ninjapear.Configuration(
host="https://nubela.co",
access_token=os.environ["NINJAPEAR_API_KEY"]
)
with ninjapear.ApiClient(config) as client:
company_api = ninjapear.CompanyAPIApi(client)
customer_api = ninjapear.CustomerAPIApi(client)
competitor_api = ninjapear.CompetitorAPIApi(client)
employee_api = ninjapear.EmployeeAPIApi(client)
contact_api = ninjapear.ContactAPIApi(client)
monitor_api = ninjapear.MonitorAPIApi(client)
meta_api = ninjapear.MetaAPIApi(client)
JavaScript
npm install ninjapear
const NinjaPear = require("ninjapear");
const client = new NinjaPear.ApiClient();
client.authentications["bearerAuth"].accessToken = process.env.NINJAPEAR_API_KEY;
const companyApi = new NinjaPear.CompanyAPIApi(client);
const customerApi = new NinjaPear.CustomerAPIApi(client);
const competitorApi = new NinjaPear.CompetitorAPIApi(client);
const employeeApi = new NinjaPear.EmployeeAPIApi(client);
const contactApi = new NinjaPear.ContactAPIApi(client);
const monitorApi = new NinjaPear.MonitorAPIApi(client);
const metaApi = new NinjaPear.MetaAPIApi(client);
Raw HTTP (other languages)
GET https://nubela.co/api/v1/{endpoint}
Authorization: Bearer YOUR_API_KEY
API Quick Reference
All endpoints are at https://nubela.co/api/v1/. The website parameter accepts either a URL (e.g. https://stripe.com) or a company name (e.g. Stripe) -- names are resolved server-side at no extra credit cost. The exception is GET /company/logo, which requires a URL/domain. For ambiguous names, prefer calling /company/website first with country_code/hint to pin the exact entity.
Company Data
| Endpoint | SDK Method | Key Params | Cost | Returns |
|---|---|---|---|---|
GET /company/details |
CompanyAPIApi.get_company_details(website, include_employee_count?, follower_count?) |
website (required), include_employee_count (bool), follower_count ("include") |
3 cr (+2 employee, +1 followers) | name, description, industry, executives, addresses, social links, public_listing |
GET /company/employee-count |
CompanyAPIApi.get_employee_count(website) |
website (required) |
2 cr | employee_count (integer) |
GET /company/updates |
CompanyAPIApi.get_company_updates(website) |
website (required) |
2 cr | blogs, x_profile, updates (blog posts and tweets) |
GET /company/funding |
CompanyAPIApi.get_company_funding(website) |
website (required) |
2 cr + 1/investor | total_funds_raised_usd, funding_rounds with investors |
GET /company/logo |
CompanyAPIApi.get_company_logo(website) |
website (required, URL/domain only) |
FREE | PNG image (128x128) |
GET /company/website |
CompanyAPIApi.get_company_website(company_name, country_code?, hint?) |
company_name (required), country_code (ISO 3166-1 alpha-2), hint (disambiguator) |
1 cr | website (canonical URL) |
Relationship Data
| Endpoint | SDK Method | Key Params | Cost | Returns |
|---|---|---|---|---|
GET /customer/listing |
CustomerAPIApi.get_customer_listing(website, cursor?, page_size?, quality_filter?) |
website (required), page_size (1-200, default 200), cursor, quality_filter (bool) |
1 cr + 2/company | customers, investors, partner_platforms (paginated) |
GET /competitor/listing |
CompetitorAPIApi.get_competitor_listing(website) |
website (required) |
2 cr/competitor (min 5 cr) | competitors with competition_reason |
People Data
| Endpoint | SDK Method | Key Params | Cost | Returns |
|---|---|---|---|---|
GET /employee/profile |
EmployeeAPIApi.get_person_profile(work_email?, first_name?, last_name?, employer_website?, role?) |
work_email OR (first_name + employer_website) OR (employer_website + role) |
3 cr | full_name, bio, work_experience, education, x_handle, location |
GET /employee/similar |
EmployeeAPIApi.get_similar_people(...) |
Same as person profile | 10 cr + 5/company searched | target profile + similar_people at competitor companies |
GET /employee/work-email |
EmployeeAPIApi.get_work_email(first_name, domain, last_name?) |
first_name (required), domain (required), last_name (improves accuracy) |
2 cr hit / 0.5 cr miss | work_email (string or null) |
Utility
| Endpoint | SDK Method | Key Params | Cost | Returns |
|---|---|---|---|---|
GET /contact/disposable-email |
ContactAPIApi.check_disposable_email(email) |
email (required) |
FREE | is_disposable_email, is_free_email |
GET /meta/credit-balance |
MetaAPIApi.get_credit_balance() |
none | FREE | credit_balance |
Monitor API (Feeds)
| Endpoint | SDK Method | Cost |
|---|---|---|
POST /monitor/feeds |
MonitorAPIApi.create_feed(request) |
3 cr |
GET /monitor/feeds |
MonitorAPIApi.list_feeds() |
FREE |
GET /monitor/feeds/{id} |
MonitorAPIApi.get_feed(feed_id) |
FREE |
DELETE /monitor/feeds/{id} |
MonitorAPIApi.delete_feed(feed_id) |
FREE |
PATCH /monitor/feeds/{id} |
MonitorAPIApi.update_feed(feed_id, request) |
FREE |
POST /monitor/feeds/{id}/targets |
MonitorAPIApi.add_target(feed_id, request) |
FREE |
PATCH /monitor/feeds/{id}/targets/{tid} |
MonitorAPIApi.update_target(feed_id, target_id, request) |
FREE |
DELETE /monitor/feeds/{id}/targets/{tid} |
MonitorAPIApi.delete_target(feed_id, target_id) |
FREE |
GET /monitor/feeds/{id}/rss.xml |
UpdatesAPIApi.get_rss_feed(feed_id, token?) |
FREE |
Ongoing monitoring costs per target per pull: blog=1 cr, website=1 cr, X/Twitter=2 cr.
Critical Integration Patterns
Timeouts
NinjaPear API calls take 30-60 seconds. Set HTTP timeout to at least 100 seconds. The Similar People endpoint can take up to 5 minutes -- set a 300-second timeout for that endpoint.
Rate Limiting
- Standard: 300 requests/minute (5-minute window, so 1500 burst per 5 min)
- Trial accounts (before first top-up): 2 requests/minute
- Handle HTTP 429 with exponential backoff
Pagination (Customer Listing)
The customer listing endpoint uses cursor-based pagination. If next_page is non-null, there are more results:
all_customers = []
cursor = None
while True:
response = customer_api.get_customer_listing(website="https://stripe.com", cursor=cursor)
all_customers.extend(response.customers or [])
if not response.next_page:
break
# Extract cursor from next_page URL
from urllib.parse import urlparse, parse_qs
cursor = parse_qs(urlparse(response.next_page).query).get("cursor", [None])[0]
Error Handling
- Only HTTP 200 charges credits (except: 404 on company/details, company/funding, and employee/profile also charges)
- HTTP 429: Rate limited -- retry with exponential backoff
- HTTP 503: Temporary failure -- retry once or twice
- HTTP 400: Bad input -- check parameters
- HTTP 401: Invalid API key
- HTTP 403: Out of credits
The website Parameter
All company-keyed endpoints (/company/details, /company/employee-count, /company/updates, /company/funding, /customer/listing, /competitor/listing) accept either a website URL (e.g., https://stripe.com) or a company name (e.g., Stripe) in the website parameter. The same applies to the employer_website parameter on /employee/profile and /employee/similar.
- Prefer URLs when known -- they're faster and unambiguous.
- Names are resolved server-side at no extra credit cost, but the inline resolver always uses
country_code=None. For ambiguous names (e.g., several companies named "Acme"), callGET /company/websitefirst (1 cr) withcountry_codeand/orhintto disambiguate, then pass the returned URL. - Exception:
GET /company/logorequires a URL/domain -- no inline name resolution.
Credit Cost Awareness
When generating integration code, add comments noting the credit cost of each API call. Example:
# Cost: 2 credits + 1 per investor returned
funding = company_api.get_company_funding(website="https://stripe.com")
Concurrent Requests
NinjaPear encourages concurrent requests for throughput. Use asyncio.gather (Python) or Promise.all (JS) when fetching data for multiple companies.
Detailed References
For full parameter specs, response schemas, and complete code examples, see:
references/api-endpoints.md-- Full endpoint parameters and response schemasreferences/sdk-usage.md-- Working Python and JS code examples for every endpointreferences/error-handling.md-- Error codes, retry patterns, rate limiting detailsreferences/monitor-api.md-- Feed monitoring CRUD reference
For the latest API documentation (may be more up-to-date than this skill):
- LLM-friendly docs: https://nubela.co/llms-full.txt
- OpenAPI 3.0 spec: https://nubela.co/api/openapi3.yaml