> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dataville.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Enrich an onboarding record with a company's filing and background

> Store a business's filing and a background article with its application at signup.

export const enrichScript = (String.raw)`import json
import os
import sys
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen


class DatavilleError(Exception):
    """Raised when Dataville returns an error other than no results."""


def lookup(source, keywords):
    """Search one Dataville source and return its data object, or None if nothing matched."""
    api_key = os.environ.get("DATAVILLE_API_KEY")
    if not api_key:
        raise DatavilleError("Set DATAVILLE_API_KEY to your Dataville API key.")
    request = Request(
        f"https://api.dataville.com/{source}/{quote(keywords, safe='')}",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    try:
        with urlopen(request) as response:
            return json.load(response)["data"]
    except HTTPError as error:
        # A 404 means the source has nothing for these keywords.
        if error.code == 404:
            return None
        raise DatavilleError(f"Dataville returned {error.code} {error.reason} for {source}.") from None


def enrich(business_name):
    """Return the filing and background fields to store with an application."""
    record = {"filing": None, "background": None, "stale": False}

    # Filing: when the business isn't an SEC filer, the edgar source returns
    # another company's filing that mentions the name, marked with a
    # search_query field. Keep only a result without one.
    filing_result = lookup("edgar", business_name)
    if filing_result and "search_query" not in filing_result["metadata"]:
        filing_metadata = filing_result["metadata"]
        record["filing"] = {
            "filer_name": filing_metadata["company_name"],
            "form_type": filing_metadata["form_type"],
            "filing_date": filing_metadata["filing_date"],
            "accession_number": filing_metadata["accession_number"],
            "document_url": filing_metadata["document_url"],
        }

    # Background: keep the article only if its title names the business and it
    # isn't a disambiguation page, whose abstract reads "X may refer to".
    # Wikipedia titles often drop a leading "The", so compare without it.
    article_result = lookup("wiki", business_name)
    if article_result:
        article_title = article_result["title"]
        abstract = article_result["metadata"]["abstract"] or ""
        normalized_name = business_name.lower().removeprefix("the ").strip()
        title_matches = normalized_name in article_title.lower()
        is_disambiguation = "may refer to" in abstract
        if title_matches and not is_disambiguation:
            record["background"] = {
                "title": article_title,
                "url": article_result["metadata"]["url"],
                "abstract": abstract,
                "last_updated": article_result["last_updated"],
            }

    # A stale result came from Dataville's stored copy while a source was down.
    # Flag the record so you can run enrich again later.
    record["stale"] = any("stale" in result for result in (filing_result, article_result) if result)
    return record


if __name__ == "__main__":
    # Try it from a terminal: python3 enrich.py "The Home Depot"
    if len(sys.argv) != 2:
        raise SystemExit('Usage: python3 enrich.py "Business legal name"')
    try:
        print(json.dumps(enrich(sys.argv[1]), indent=2))
    except DatavilleError as error:
        raise SystemExit(error)
`;

When a business signs up, you can look up its most recent SEC filing and its Wikipedia article and store both with the application. A reviewer can then open the filing and the article from the record instead of searching for them.

In this guide, you'll add a Python function to your signup flow that runs both lookups through Dataville and returns the fields to store.

## Before you start

You'll need:

* A Dataville account and its API key, from the [API Keys](https://app.dataville.com/api-keys) page.
* Python 3.
* A signup form that asks for the business's full legal name.

## Add the lookups to your signup flow

You can try the function from a terminal before you add it to your signup code.

<Steps>
  <Step title="Download the enrichment script">
    The script has an `enrich` function that looks up a business in SEC EDGAR and Wikipedia, checks that each result is about that business, and returns the fields to store. Download it into the folder that holds your signup code.

    <a href={"data:text/x-python;charset=utf-8," + encodeURIComponent(enrichScript)} download="enrich.py" className="block">
      <Card title="enrich.py" icon="file-code">
        Download the enrichment script.
      </Card>
    </a>

    <Accordion title="View enrich.py" icon="code">
      ```python theme={null}
      import json
      import os
      import sys
      from urllib.error import HTTPError
      from urllib.parse import quote
      from urllib.request import Request, urlopen


      class DatavilleError(Exception):
          """Raised when Dataville returns an error other than no results."""


      def lookup(source, keywords):
          """Search one Dataville source and return its data object, or None if nothing matched."""
          api_key = os.environ.get("DATAVILLE_API_KEY")
          if not api_key:
              raise DatavilleError("Set DATAVILLE_API_KEY to your Dataville API key.")
          request = Request(
              f"https://api.dataville.com/{source}/{quote(keywords, safe='')}",
              headers={"Authorization": f"Bearer {api_key}"},
          )
          try:
              with urlopen(request) as response:
                  return json.load(response)["data"]
          except HTTPError as error:
              # A 404 means the source has nothing for these keywords.
              if error.code == 404:
                  return None
              raise DatavilleError(f"Dataville returned {error.code} {error.reason} for {source}.") from None


      def enrich(business_name):
          """Return the filing and background fields to store with an application."""
          record = {"filing": None, "background": None, "stale": False}

          # Filing: when the business isn't an SEC filer, the edgar source returns
          # another company's filing that mentions the name, marked with a
          # search_query field. Keep only a result without one.
          filing_result = lookup("edgar", business_name)
          if filing_result and "search_query" not in filing_result["metadata"]:
              filing_metadata = filing_result["metadata"]
              record["filing"] = {
                  "filer_name": filing_metadata["company_name"],
                  "form_type": filing_metadata["form_type"],
                  "filing_date": filing_metadata["filing_date"],
                  "accession_number": filing_metadata["accession_number"],
                  "document_url": filing_metadata["document_url"],
              }

          # Background: keep the article only if its title names the business and it
          # isn't a disambiguation page, whose abstract reads "X may refer to".
          # Wikipedia titles often drop a leading "The", so compare without it.
          article_result = lookup("wiki", business_name)
          if article_result:
              article_title = article_result["title"]
              abstract = article_result["metadata"]["abstract"] or ""
              normalized_name = business_name.lower().removeprefix("the ").strip()
              title_matches = normalized_name in article_title.lower()
              is_disambiguation = "may refer to" in abstract
              if title_matches and not is_disambiguation:
                  record["background"] = {
                      "title": article_title,
                      "url": article_result["metadata"]["url"],
                      "abstract": abstract,
                      "last_updated": article_result["last_updated"],
                  }

          # A stale result came from Dataville's stored copy while a source was down.
          # Flag the record so you can run enrich again later.
          record["stale"] = any("stale" in result for result in (filing_result, article_result) if result)
          return record


      if __name__ == "__main__":
          # Try it from a terminal: python3 enrich.py "The Home Depot"
          if len(sys.argv) != 2:
              raise SystemExit('Usage: python3 enrich.py "Business legal name"')
          try:
              print(json.dumps(enrich(sys.argv[1]), indent=2))
          except DatavilleError as error:
              raise SystemExit(error)
      ```
    </Accordion>
  </Step>

  <Step title="Try it on a public company">
    Set your API key, then run the script with a business's legal name:

    ```bash theme={null}
    export DATAVILLE_API_KEY=dataville_xxxxxxxxxxxxxxxx
    python3 enrich.py "The Home Depot"
    ```

    The script prints the record that `enrich` returns. Here is the record for The Home Depot on September 25, 2026, with the abstract shortened:

    ```json theme={null}
    {
      "filing": {
        "filer_name": "HOME DEPOT, INC.",
        "form_type": "10-Q",
        "filing_date": "2026-08-25",
        "accession_number": "0001628280-26-058715",
        "document_url": "https://www.sec.gov/Archives/edgar/data/354950/000162828026058715/hd-20260802.htm"
      },
      "background": {
        "title": "Home Depot",
        "url": "https://en.wikipedia.org/wiki/Home_Depot",
        "abstract": "The Home Depot, Inc. is an American multinational home improvement retail corporation...",
        "last_updated": "2026-09-24T11:40:22.000Z"
      },
      "stale": false
    }
    ```

    `filing` holds the business's most recent 10-K, 10-Q, or 8-K, and `document_url` links to it on sec.gov. `background` holds the Wikipedia article's title, link, and opening paragraph.
  </Step>

  <Step title="Try it on a private company">
    Most businesses that sign up aren't SEC filers. Run the script with the example private company:

    ```bash theme={null}
    python3 enrich.py "Northwind Payments LLC"
    ```

    Northwind Payments LLC is a made-up company, so there's nothing to store:

    ```json theme={null}
    {
      "filing": null,
      "background": null,
      "stale": false
    }
    ```

    The `edgar` source returned another company's filing that mentions the name, a 2020 filing by a mortgage trust, and `enrich` skips a result like that. Wikipedia has no article about the business, so `background` is empty too.

    Use the business's full legal name. A short name can match an article about something else: `Oracle` returns Wikipedia's article about oracles in divination, and the title check can't tell it apart from the company. `Oracle Corporation` returns the company's article.
  </Step>

  <Step title="Call it from your signup flow">
    Import `enrich` in your signup code and store what it returns with the application. A failed lookup shouldn't stop a signup, so catch `DatavilleError` and look the business up again later:

    ```python theme={null}
    from enrich import DatavilleError, enrich


    def add_business_record(application):
        """Add the business's filing and background to a new application."""
        try:
            application["business_record"] = enrich(application["legal_name"])
        except DatavilleError as error:
            # Keep the signup going, and look the business up again later.
            application["business_record"] = None
            application["enrichment_error"] = str(error)
        return application
    ```

    `enrich` raises `DatavilleError` for anything other than a result or no result, such as **401** for a wrong API key or **429** when you reach a limit. To learn more about each error, see [Troubleshooting](/api-reference/errors).

    If a source is unavailable, Dataville answers from its stored copy and `enrich` sets `stale` to `true`. Run `enrich` again for those records later, and replace the stored fields once `stale` is `false`. See [Response format](/response-format#stale-results) to learn more about stale results.
  </Step>
</Steps>

## Next steps

Once you've approved a business, you can watch it for new filings.

<CardGroup cols={2}>
  <Card title="Monitor SEC filings for a watchlist" icon="building-columns" href="/guides/filings-watchlist">
    Check the companies you've approved for new filings every weekday morning.
  </Card>

  <Card title="Response format" icon="brackets-curly" href="/response-format">
    Every field in the envelope, and how Dataville reports errors and stale results.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.