Home › Implementation › IP to Company API Python Example: Single and Bulk Lookups
IP to Company API Python Example: Single and Bulk Lookups
A minimal IP to company integration in Python reads the API key from an environment variable, sends a bearer-authenticated GET for one address, then branches on the verdict field. For many addresses, chunk them into batches of 100 and post to the bulk route. The sketch below follows BusinessMCP’s documented routes.
What the API looks like
Per the IP to company API page:
- Single lookup:
GET /api/hub/v1/ip/{ip} - Bulk lookup:
POST /api/hub/v1/ip/bulkwith{"ips": [...]}of 1 to 100 addresses - Auth: bearer API key
- Verdicts:
company,non_business,unidentified - Price: $0.02 per lookup, charged only when a result is found, with $5 free credit for 7 days
The page does not list every response field; those are in the API documentation. The code below only relies on verdict and, for named companies, a company object with a domain, as the page describes. Check field names against the live reference before shipping.
Setup
pip install requests
export BUSINESSMCP_API_KEY="your-key"
export IP_API_BASE="https://businessmcp.com" # confirm the host in the docs
Using a variable for the base URL means you can change it without touching code.
Single lookup
import os
import requests
BASE = os.environ["IP_API_BASE"]
KEY = os.environ["BUSINESSMCP_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}
def lookup(ip: str, timeout: float = 5.0) -> dict | None:
r = requests.get(f"{BASE}/api/hub/v1/ip/{ip}", headers=HEADERS, timeout=timeout)
if r.status_code != 200:
return None # log it; do not treat as a company
return r.json()
def company_or_none(data: dict | None):
if not data or data.get("verdict") != "company":
return None # everything else is unknown
return data.get("company")
The key rule sits in company_or_none: anything other than company is unknown, which is exactly what the product documentation advises.
Bulk lookup with chunking
import time
def chunks(items, n=100):
for i in range(0, len(items), n):
yield items[i:i + n]
def bulk(ips, retries=3):
results = []
for batch in chunks(sorted(set(ips))):
for attempt in range(retries):
try:
r = requests.post(f"{BASE}/api/hub/v1/ip/bulk",
headers=HEADERS, json={"ips": batch}, timeout=30)
if r.status_code == 200:
results.append(r.json())
break
except requests.RequestException:
pass
time.sleep(2 ** attempt) # simple backoff
return results
Notes on the design:
sorted(set(ips))deduplicates and gives a stable order.- Each batch is retried a few times with backoff, then skipped; log skipped batches so you can re-run them.
- The response shape for bulk is described as the same fields as a single lookup for each address, but confirm how results are wrapped before parsing.
Key takeaway: the integration is small. The work is in what you do around it: dedupe, filter infrastructure, store every verdict and respect retention.
Wiring it to real traffic
| Step | Where | Detail |
|---|---|---|
| Get client IP | Edge or app | Use the forwarded address only when the request verifiably came through your proxy |
| Skip private ranges | Before the call | Use the ipaddress module’s is_private and is_loopback |
| Deduplicate | Job level | Per day, per address |
| Resolve | API call | Single for live, bulk for batch |
| Store | Database | Verdict, class, name, domain, date |
| Expire | Scheduler | Re-resolve after a few days |
A small helper for the second step:
import ipaddress
def routable(ip: str) -> bool:
try:
a = ipaddress.ip_address(ip)
except ValueError:
return False
return not (a.is_private or a.is_loopback or a.is_link_local or a.is_reserved)
Testing the integration
- Run it against five addresses you know: your office, a home line, a mobile hotspot, a cloud VM and a known customer’s office if you have one.
- Confirm that only the office and customer rows return
company. - Compare your balance before and after to see how billing treats the others.
- Feed it a malformed string and confirm the code does not crash or bill.
For a broader evaluation across vendors, use the method in how to test an IP to company API. For cost projections see IP to company API pricing.
Production hardening
- Set explicit timeouts, as shown, so a slow upstream never hangs a request thread.
- Never block a page render on a lookup; resolve asynchronously after the response.
- Cache results by IP with a short expiry.
- Redact the key from logs and error reports.
- Keep raw addresses only as long as your retention policy allows; see GDPR and IP lookup.
- Monitor the share of
companyverdicts over time; a sudden jump or drop signals a bug, such as logging a proxy address.
Once results flow in, you can feed them into lead scoring or traffic-quality reports like those in filtering bot and cloud traffic.
Reading the results into a table
Once you have responses, a small function flattens them into rows you can load into a spreadsheet or database. Adjust the keys to the response format in the API documentation.
def to_row(ip: str, data: dict) -> dict:
company = data.get("company") or {}
return {
"ip": ip,
"verdict": data.get("verdict"),
"company_name": company.get("name"),
"company_domain": company.get("domain"),
}
The .get() calls matter. A field you expect may be absent for non-company verdicts, and code that indexes directly will crash on the first ISP address. Write defensively, store the raw response alongside the flattened row, and you can re-parse later if the schema changes or you want fields you ignored the first time.
Unit tests worth writing
- A
companyresponse returns a name and domain. - A
non_businessresponse returnsNonefromcompany_or_none. - An
unidentifiedresponse returnsNone. - A 500 response is retried and then skipped without raising.
- A malformed address never reaches the network call.
Mock the HTTP layer with a small fake rather than calling the real API in tests, so your suite stays free and fast. Keep one slow integration test, run manually, that checks your control addresses against the live service. The control-set idea is described in how to test an IP to company API.
Alternatives to hand-rolled code
For a one-off analysis, you may not need a script. A spreadsheet with a column of addresses and a small macro, or the free web lookup for a handful of addresses, is often faster. Write code when the job repeats, when volume passes a few hundred addresses, or when the result must flow into another system. When you do, keep the client in one small module so that swapping vendors later means changing one file and a field mapping rather than rewriting your pipeline.
Frequently asked questions
How do I call an IP to company API from Python?
Send an authenticated HTTP request with the requests library, parse the JSON response and branch on the verdict field. Handle timeouts and non-200 responses explicitly.
Where should I store the API key?
In an environment variable or secrets manager, never in source code. BusinessMCP’s examples read it from BUSINESSMCP_API_KEY.
How do I handle rate limits?
Retry with exponential backoff and a cap. The product page does not state rate limits, so check the documentation and test.
Should the code store the raw IP?
Only as long as needed. Keep the company-level result and drop or truncate the raw address in line with your retention policy.