Skip to main content
Use this page for complete, ready-to-copy workflow examples that chain multiple Company API calls together. Each workflow shows the request, the expected response, what to extract, and how to handle failures. For individual endpoint examples, see:

Workflow 1: Autocomplete → Search → Enrich

Goal: Find US-based software companies and get full profiles for the top matches. Why this workflow: You do not know the exact industry value the Search API expects. Autocomplete discovers it, Search finds matching companies, and Enrich fills in the details.

Step 1: Discover valid industry values

curl --request POST \
  --url https://api.crustdata.com/company/search/autocomplete \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{"field": "basic_info.industries", "query": "software", "limit": 3}'
Extract: Take suggestions[0].value"Software Development". Use this exact string in your Search filter. If empty: If suggestions is [], your query did not match any indexed values. Try a broader term (e.g., "tech" instead of "software engineering").

Step 2: Search for matching companies

curl --request POST \
  --url https://api.crustdata.com/company/search \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{
    "filters": {
      "op": "and",
      "conditions": [
        {"field": "basic_info.industries", "type": "in", "value": ["Software Development"]},
        {"field": "locations.hq_country", "type": "=", "value": "USA"}
      ]
    },
    "sorts": [{"column": "headcount.total", "order": "desc"}],
    "limit": 3,
    "fields": ["crustdata_company_id", "basic_info.name", "basic_info.primary_domain", "headcount.total"]
  }'
Extract: Take companies[].crustdata_company_id values → [12345, 67890, 628895]. Pass these to Enrich. If empty: If companies is [], no companies matched your filters. Broaden your conditions or use Autocomplete to check that your filter values are valid. To get more results: Pass next_cursor as cursor in the next request. Stop paginating when next_cursor is null.

Step 3: Enrich the top matches

curl --request POST \
  --url https://api.crustdata.com/company/enrich \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{
    "crustdata_company_ids": [12345, 67890, 628895],
    "fields": ["basic_info", "headcount", "funding", "hiring"]
  }'
Extract: Each item in results corresponds to one input ID. Access the profile via results[i].matches[0].company_data. If a match is empty: If matches is [] for an identifier, that company was not found. The request still succeeds (200 OK) for the other identifiers.

Workflow 2: Inbound domain → Identify → Search for similar companies

Goal: An inbound lead arrives from retool.com. Find similar companies for prospecting. Why this workflow: Identify resolves the domain to a company record, then Search finds similar companies based on the identified company’s industry and size.

Step 1: Identify the inbound company

curl --request POST \
  --url https://api.crustdata.com/company/identify \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{"domains": ["retool.com"]}'
Extract: Take results[0].matches[0].company_data.basic_info.industries[0]"Software Development" and employee_count_range"201-500".

Step 2: Search for similar companies

curl --request POST \
  --url https://api.crustdata.com/company/search \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{
    "filters": {
      "op": "and",
      "conditions": [
        {"field": "basic_info.industries", "type": "in", "value": ["Software Development"]},
        {"field": "headcount.total", "type": ">", "value": 200},
        {"field": "headcount.total", "type": "<", "value": 1000}
      ]
    },
    "sorts": [{"column": "headcount.total", "order": "desc"}],
    "limit": 10,
    "fields": ["crustdata_company_id", "basic_info.name", "basic_info.primary_domain", "headcount.total"]
  }'
Extract: Take companies[].crustdata_company_id values and pass them to Enrich for full profiles of promising matches. If empty: If companies is [], broaden your filters (e.g., wider headcount range or more industries). Use Autocomplete to verify valid filter values.

Error handling patterns

All Company API endpoints return structured errors. Here are the patterns to handle:

Invalid request (400)

{
    "error": {
        "type": "invalid_request",
        "message": "Unsupported columns in conditions: ['nonexistent_field']",
        "metadata": []
    }
}
Action: Fix the request. Do not retry 400 errors — they indicate a malformed request.

Invalid API key (401)

{ "message": "Invalid API key in request" }
Action: Check your API key. The 401 response uses a different shape than other errors.

No match (Enrich/Identify)

When no company matches, matches is empty but the request succeeds:
{
    "results": [
        {
            "matched_on": "nonexistent-domain.com",
            "match_type": "domain",
            "matches": []
        }
    ]
}
Action: Try a different identifier type or check for typos. Do not retry — the company may not be in the database.
The OpenAPI spec also defines 404 for Enrich and Identify. Current platform behavior returns 200 with empty matches. Handle both. See Conventions.

Partial batch failure (Enrich)

When enriching multiple identifiers, some may match and others may not. The request succeeds with 200:
{
    "results": [
        {
            "matched_on": "hubspot.com",
            "match_type": "domain",
            "matches": [
                {
                    "confidence_score": 1.0,
                    "company_data": {
                        "basic_info": {
                            "name": "HubSpot",
                            "primary_domain": "hubspot.com"
                        }
                    }
                }
            ]
        },
        {
            "matched_on": "nonexistent-domain.com",
            "match_type": "domain",
            "matches": []
        }
    ]
}
Action: Iterate over results. For each entry, check matches.length > 0 before accessing company_data. Log or retry unmatched identifiers separately.

Server error (500)

{
    "error": {
        "type": "internal_error",
        "message": "An unexpected error occurred",
        "metadata": []
    }
}
Action: Retry with exponential backoff: wait 1s, then 2s, then 4s. Stop after 3 retries. If the error persists, contact support.

Retry decision table

StatusRetry?Action
400NoFix the request
401NoCheck API key
403NoCheck permissions or credits
404NoTry different identifier
500YesExponential backoff (1s, 2s, 4s)
Rate limitYesWait 60 seconds, then retry

Workflow 3: Paginated search with stable sorting

Goal: Page through all mid-size US software companies using cursor-based pagination. Why this workflow: Large result sets require pagination. Always include sorts for stable ordering across pages.
curl --request POST \
  --url https://api.crustdata.com/company/search \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{
    "filters": {
      "op": "and",
      "conditions": [
        {"field": "basic_info.industries", "type": "in", "value": ["Software Development"]},
        {"field": "headcount.total", "type": ">", "value": 200},
        {"field": "headcount.total", "type": "<", "value": 1000},
        {"field": "locations.hq_country", "type": "=", "value": "USA"}
      ]
    },
    "sorts": [{"column": "crustdata_company_id", "order": "asc"}],
    "limit": 100,
    "fields": ["crustdata_company_id", "basic_info.name", "basic_info.primary_domain"]
  }'
Pagination loop logic:
  1. Send the first request without cursor.
  2. Extract next_cursor from the response.
  3. If next_cursor is null, stop — you have all results.
  4. Otherwise, pass next_cursor as cursor in the next request. Keep filters, sorts, limit, and fields the same.
  5. Repeat until next_cursor is null.
Always include sorts when paginating. Without sorts, result ordering is not guaranteed and pages may contain duplicates or gaps.
For more on pagination, error handling, and retry guidance, see Conventions.