You can clean company names in Python before they turn into awkward email copy. In this guide, you will send eight names to Datablist's Company Name Cleaner API, wait for the background task, and read every result page. Then you will reuse the same function to turn raw company names from a lead provider into natural email introductions.

This approach handles more than a fixed list of suffixes. It can fix casing and spacing, remove a separate legal form or an obvious tagline, and preserve accents, numbers, acronyms, and brand punctuation. Keep the raw name on each lead, then use the cleaned value where people will read it.

📌 Summary For Those In a Rush

Use the Company Name Cleaner API to turn raw lead-provider company names into readable email introductions while preserving the source data. The Python example first cleans eight names, then reuses the same function to prepare personalization copy.

  • Input: Source names such as TRansformFactory Limited, AT&T, and a directory name containing a tagline.
  • Output: A cleaned name and confidence value, followed by an introduction such as your team at NGF Immobilien.

Set up your API key and Python environment

Create a Datablist Personal API Key. The key follows this shape:

text
auth0|exampleuser:exampleaccount:EXAMPLE_TOKEN_NOT_VALID:1

That value is an example, not a working key. Export your own key as DATABLIST_API_KEY:

shell
export DATABLIST_API_KEY="your-personal-api-key"

REST API access is available on the Growth plan. Cleaner calls also use enrichment credits, which I explain in the cost section.

For this example, I use the requests package. Create a file named clean_company_names.py, install the package, and run the file with:

shell
python -m pip install requests
python clean_company_names.py

Build the clean_company_names() function

Paste this complete script into clean_company_names.py:

python
import json
import os
import time
import uuid
import requests
def clean_company_names(company_records, api_key):
"""Submit one company-name task, wait, and return every result."""
tasks_url = "https://api.datablist.com/v1/enrichments/tasks"
headers = {"X-API-Key": api_key}
inputs = [
{
"inputId": record["id"],
"data": {"companyName": record["company_name"]},
}
for record in company_records
]
request_id = str(uuid.uuid4())
print(f"Request ID: {request_id}", flush=True)
payload = {
"enrichmentId": "company-name-cleaner",
"uniqueId": request_id,
"settings": {"keepLegalSuffix": False},
"inputs": inputs,
}
response = requests.post(tasks_url, headers=headers, json=payload)
response.raise_for_status()
task = response.json()
task_id = task["@id"]
task_url = f"{tasks_url}/{task_id}"
while task["status"] not in ("completed", "failed", "stopped"):
time.sleep(3)
response = requests.get(task_url, headers=headers)
response.raise_for_status()
task = response.json()
if task["status"] != "completed":
raise RuntimeError(
f"Task {task_id} {task['status']}: {task.get('error')}"
)
results = []
results_url = f"{task_url}/results"
params = {"limit": 100}
while True:
response = requests.get(results_url, headers=headers, params=params)
response.raise_for_status()
page = response.json()
results.extend(page["results"])
next_cursor = page["nextCursor"]
if next_cursor is None:
break
params["cursor"] = next_cursor
return results
if __name__ == "__main__":
api_key = os.environ.get("DATABLIST_API_KEY")
if not api_key:
raise RuntimeError("Set DATABLIST_API_KEY before running this script.")
companies = [
{"id": "transform-factory", "company_name": "TRansformFactory Limited"},
{"id": "ngf-immobilien", "company_name": "NGF Immobilien GmbH & Co. KG"},
{"id": "lensen-partner", "company_name": "Lenßen + Partner Immobilien"},
{"id": "studio-54", "company_name": "Studio 54"},
{"id": "att", "company_name": "AT&T"},
{"id": "ebay", "company_name": "ebay"},
{
"id": "acme-tagline",
"company_name": "Acme | Leading provider of solar installations",
},
{
"id": "boston-consulting-group",
"company_name": "Boston Consulting Group",
},
]
cleaning_results = clean_company_names(companies, api_key)
for result in cleaning_results:
print(json.dumps(result, ensure_ascii=False))

The function keeps the three API steps in execution order.

First, it turns each company into an API input. The inputId is your stable record ID, while data.companyName is the value to clean. A stable ID matters because you should map a result back to its record by ID, not by its position in the response.

For one company, the important part of the submitted JSON looks like this:

json
{
"enrichmentId": "company-name-cleaner",
"uniqueId": "your-generated-request-id",
"settings": {
"keepLegalSuffix": false
},
"inputs": [
{
"inputId": "transform-factory",
"data": {
"companyName": "TRansformFactory Limited"
}
}
]
}

You can submit several inputs in the same task. Each one remains independent, so a problem with one name does not change the inputId or source data for the others. The script builds this array from companies, which means you can swap in IDs and names from your own application without changing the API sequence.

Next, the POST request creates a task with the company-name-cleaner enrichment. The script prints its generated uniqueId before making that request. Keep that value: it helps you find the task if your connection drops before you receive the creation response.

The API accepts the request with HTTP 202 and returns a task. The response includes an @id, its current status, and URLs for the task and its results. A shortened response looks like this:

json
{
"@id": "task-id",
"enrichmentId": "company-name-cleaner",
"uniqueId": "your-generated-request-id",
"status": "queued",
"links": {
"self": "https://api.datablist.com/v1/enrichments/tasks/task-id",
"results": "https://api.datablist.com/v1/enrichments/tasks/task-id/results"
}
}

The function reads @id and builds the same task URLs itself. You do not need to copy an ID between requests when the script receives the creation response normally.

The API processes the task in the background. This example uses blocking Requests calls, so the script sleeps for three seconds between status checks and does not continue until the task reaches completed, failed, or stopped.

Finally, the function requests 100 results at a time. When nextCursor contains a value, it sends that value as cursor and fetches the next page. It stops only when nextCursor is null. You can find the synchronous Requests example and an async HTTPX example in the Company Name Cleaner API GitHub repository.

Read the results

Here is the result for TRansformFactory Limited:

json
{
"inputId": "transform-factory",
"status": "success",
"outputs": {
"cleanedCompanyName": "Transform Factory",
"companyNameConfidence": "High"
},
"creditsUsed": "0.5",
"error": null
}

The eight examples show why cleaning a display name takes more than removing Inc. or converting everything to title case.

Source company nameCleaned company name
TRansformFactory LimitedTransform Factory
NGF Immobilien GmbH & Co. KGNGF Immobilien
Lenßen + Partner ImmobilienLenßen + Partner Immobilien
Studio 54Studio 54
AT&TAT&T
ebayeBay
Acme | Leading provider of solar installationsAcme
Boston Consulting GroupBoston Consulting Group

Every result in this task returned success, High confidence, and 0.5 credits. The task used 4 credits in total. Confidence gives you a practical review signal. You can use a High-confidence name directly in this small example, while Low or Very Low results are worth comparing with the source name before you put them into email copy.

A task can also contain no_result, error, or skipped items. Read each item's status before using outputs, and read error when the status is not success. The email example below creates an introduction only when the status is success and cleanedCompanyName is not empty. An unsuccessful lead stays visible without message copy while successful names continue.

The script sets keepLegalSuffix to false, which is the useful default for conversational display names. It removed Limited and GmbH & Co. KG when they were separate from the brand. If you set it to true, the cleaner keeps an existing legal form in a shorter conventional form when appropriate. It does not add a legal form that was missing.

Company Name Cleaner removes legal forms and taglines while preserving brand punctuation and returning a cleaned name with confidence
Company Name Cleaner removes legal forms and taglines while preserving brand punctuation and returning a cleaned name with confidence

See the Company Name Cleaner reference for current behavior and settings. If you prefer to clean names in the Datablist interface, the company-name cleaning methods guide covers the no-code options.

Use cleaned names in email introductions

The direct call gives you clean names. The practical next step is to put them between lead delivery and email preparation.

Imagine a lead provider sends a record for NGF Immobilien GmbH & Co. KG. Starting an email with “your team at NGF Immobilien GmbH & Co. KG” sounds like you pasted a database field. Cleaning it first gives you “your team at NGF Immobilien” while the provider's raw value stays available on the record.

To run this follow-up on its own, keep the imports and clean_company_names() function from the complete script. Replace its if __name__ == "__main__": block with the code below:

python
if __name__ == "__main__":
api_key = os.environ.get("DATABLIST_API_KEY")
if not api_key:
raise RuntimeError("Set DATABLIST_API_KEY before running this script.")
leads = [
{
"lead_id": "lead-001",
"first_name": "Maya",
"company_name": "TRansformFactory Limited",
},
{
"lead_id": "lead-002",
"first_name": "Jonas",
"company_name": "NGF Immobilien GmbH & Co. KG",
},
{
"lead_id": "lead-003",
"first_name": "Alex",
"company_name": "AT&T",
},
{
"lead_id": "lead-004",
"first_name": "Priya",
"company_name": "Acme | Leading provider of solar installations",
},
]
company_records = [
{"id": lead["lead_id"], "company_name": lead["company_name"]}
for lead in leads
]
cleaning_results = clean_company_names(company_records, api_key)
results_by_id = {
result["inputId"]: result for result in cleaning_results
}
for lead in leads:
result = results_by_id.get(lead["lead_id"])
if not result or result["status"] != "success":
continue
cleaned_name = result.get("outputs", {}).get("cleanedCompanyName")
if cleaned_name:
lead["email_intro"] = (
f"Hi {lead['first_name']}, I wanted to reach out to "
f"your team at {cleaned_name}."
)
print(json.dumps(leads, indent=2, ensure_ascii=False))

The list comprehension adapts each lead to the same id and company_name shape used in the first example. This submits one task with the same clean_company_names() function. The follow-up adds only the message-building logic.

The result index is keyed by inputId, so the code does not depend on response order. If lead-002 comes back after another record, its result still maps to Jonas. The API changes NGF Immobilien GmbH & Co. KG to NGF Immobilien, and the code produces:

text
Hi Jonas, I wanted to reach out to your team at NGF Immobilien.

The raw company_name remains NGF Immobilien GmbH & Co. KG. If a result is unsuccessful or has no cleaned name, the lead remains in the output without an email_intro, ready for you to inspect.

Here is the resulting list:

json
[
{
"lead_id": "lead-001",
"first_name": "Maya",
"company_name": "TRansformFactory Limited",
"email_intro": "Hi Maya, I wanted to reach out to your team at Transform Factory."
},
{
"lead_id": "lead-002",
"first_name": "Jonas",
"company_name": "NGF Immobilien GmbH & Co. KG",
"email_intro": "Hi Jonas, I wanted to reach out to your team at NGF Immobilien."
},
{
"lead_id": "lead-003",
"first_name": "Alex",
"company_name": "AT&T",
"email_intro": "Hi Alex, I wanted to reach out to your team at AT&T."
},
{
"lead_id": "lead-004",
"first_name": "Priya",
"company_name": "Acme | Leading provider of solar installations",
"email_intro": "Hi Priya, I wanted to reach out to your team at Acme."
}
]

This example prepares the text but does not send an email. For the wider outreach workflow, see how to normalize company names for cold outreach.

Estimate the API cost

Each successfully cleaned, uncached name uses 0.5 credits. Cached results and failed inputs use zero credits. This means 1,000 successful uncached names use 500 credits.

Loading pricing…

API access requires the Growth plan, which currently includes monthly credits. Datablist uses included credits before purchased top-ups. At the current USD base top-up price of $20 for 20,000 credits, 500 credits cost $0.50. Check the pricing page for the current subscription and credit-package prices, especially if your account uses EUR pricing.

Fix common errors and recover a task

The example stops instead of hiding an HTTP or task error. Use the point of failure to choose the next action:

  • Missing environment variable: the script raises RuntimeError before it sends a request. Export DATABLIST_API_KEY and run it again.
  • Invalid API key: raise_for_status() reports the failed API request. Create or copy the Personal API Key again and confirm that the script sends it in X-API-Key.
  • Insufficient credits: add credits or wait for your included credits to renew, then decide whether to create a new task.
  • Unsuccessful item: inspect its status and error. Leave that lead without an email_intro rather than building copy from an empty value.
  • Failed or stopped task: inspect the task and any available results and credit use before submitting the names again.

If the connection drops while you create a task, the script has already printed its uniqueId. List your tasks first to check their status before creating another task. Put the following snippet in a separate file after copying the imports and api_key setup from the main script. It lists recent cleaner tasks, matches the printed value locally, and shows the returned @id:

python
response = requests.get(
"https://api.datablist.com/v1/enrichments/tasks",
headers={"X-API-Key": api_key},
params={"enrichmentId": "company-name-cleaner", "limit": 100},
)
response.raise_for_status()
request_id = "paste-the-printed-request-id"
tasks = response.json()["tasks"]
matching_task = None
for task in tasks:
if task["uniqueId"] == request_id:
matching_task = task
break
if matching_task:
print("Task ID:", matching_task["@id"])

Task history can be filtered by enrichmentId, but not by uniqueId. If your task is not on the first page, follow the history response's nextCursor and check the next page. Once you have the task ID, use the same status and results URLs from the main function. The task documentation explains task states, result access, and interruption behavior.

Conclusion: use cleaned names in your email copy

You now have one readable Python function that submits company names, waits for the Datablist task, retrieves all result pages, and returns a confidence value. The lead example reuses those results to create natural email introductions while leaving every raw company name intact.

Run the message-building example over a small sample from your latest lead provider delivery next. Compare Low or Very Low confidence names with their source values before you use the introductions in your outreach workflow.