Understand the Crunchbase data model
Connect entities, fields, and cards in three requests.
Retrieve a company and identify its founders. In three requests, you will build a company-and-founder summary and learn how to follow relationships between Crunchbase records.
Before you start: complete Make your first API request. These examples use cURL in a terminal, the same crunchbase organization and your provisioned REST API key. Set CRUNCHBASE_API_KEY to that key in your local environment before running the commands. Check the reference for your licensed package before adapting the examples to other collections, fields, or cards.
Set your API key in the terminal
Set the variable in the terminal where you will run the examples, replacing YOUR_API_KEY with your actual key.
macOS/Linux (bash or zsh):
export CRUNCHBASE_API_KEY='YOUR_API_KEY'Windows PowerShell (for the Python example):
$env:CRUNCHBASE_API_KEY='YOUR_API_KEY'The cURL commands below use bash/zsh syntax; Windows users can follow the Python example instead.
The path you will follow
%%{init: {"theme":"base","themeCSS":".edgeLabel .label, .edgeLabel p { background-color: var(--cb-diagram-label) !important; color: var(--cb-diagram-text) !important; } .edgeLabel rect { fill: var(--cb-diagram-label) !important; opacity: 1 !important; }","themeVariables":{"fontFamily":"monospace","fontSize":"16px","primaryColor":"#edf5ff","primaryTextColor":"#173b5e","primaryBorderColor":"#82afe0","lineColor":"#6d94bf","edgeLabelBackground":"#edf5ff"},"flowchart":{"curve":"basis","padding":18,"htmlLabels":true}}}%%
flowchart LR
A("1. Organization: Crunchbase") -->|"Add<br/>card_ids=founders"| B("2. Founders<br/>card")
B -->|"Copy founder UUID"| C("3. Person: Michael Arrington")
classDef cbModelNode stroke-width:1.5px,rx:14,ry:14;
class A,B,C cbModelNode;
Start with organizations/crunchbase, request its founders card, then copy a returned cards.founders[0].identifier.uuid into people/{uuid}. The founder UUID—not the organization UUID—connects the second request to the third.
1. Retrieve the organization's fields
A collection groups records of one type; an entity is one record. Here, organizations is the collection and crunchbase identifies the company. Select three fields, or named pieces of data, with field_ids:
curl --request GET \
'https://api.crunchbase.com/v4/data/entities/organizations/crunchbase?field_ids=identifier,short_description,website_url' \
--header "X-cb-user-key: $CRUNCHBASE_API_KEY" \
--header 'Accept: application/json'Check: the response is HTTP 200. Read the requested fields under properties.
Response excerpt; other fields omitted:
{
"properties": {
"identifier": {
"uuid": "43ac6c2f-65ec-5b40-89d6-ab9d4c4988ef",
"value": "Crunchbase",
"permalink": "crunchbase"
},
"website_url": "https://www.crunchbase.com",
"short_description": "Crunchbase is a predictive private company intelligence solution, powered by live company data, AI, and market activity from 80M+ users."
}
}The organization UUID is at properties.identifier.uuid. The website is at properties.website_url. The description is at properties.short_description. Profile text can change; compare the structure and identifiers rather than expecting identical wording.
If your result differs: for a failed request, use the HTTP recovery table. For HTTP 200 with a missing field, check the exact field_ids spelling and your package’s schema. Keep missing optional values as missing; stop if the entity identifier is absent.
2. Add the founders card
A card is a named group of additional data on an entity, often related records. Keep the organization’s identifier and add card_ids=founders to retrieve its founders:
curl --request GET \
'https://api.crunchbase.com/v4/data/entities/organizations/crunchbase?field_ids=identifier&card_ids=founders' \
--header "X-cb-user-key: $CRUNCHBASE_API_KEY" \
--header 'Accept: application/json'Check: the response is HTTP 200. The organization's identifier remains under properties; the founder records are under cards.founders.
Response excerpt; other fields omitted:
{
"properties": {
"identifier": {
"uuid": "43ac6c2f-65ec-5b40-89d6-ab9d4c4988ef",
"value": "Crunchbase",
"permalink": "crunchbase"
}
},
"cards": {
"founders": [
{
"identifier": {
"uuid": "512f4cc3-76bf-85b6-67cd-4a2ee14f2c75",
"value": "Michael Arrington",
"permalink": "michael-arrington",
"entity_def_id": "person"
}
}
]
}
}For the first founder, read cards.founders[0].identifier.uuid. Here, [0] means the first array item. Check that the array contains an item before accessing it. This response identifies Michael Arrington as a person; the Entity Lookup collection for people is people.
field_ids=identifier selects the organization field in this request. It does not restrict each founder to that field. A founder’s fields are directly inside the card item, without a properties wrapper.
Before continuing: if cards.founders is missing or null, check card_ids=founders and package access; do not conclude that the company has no founders. If it is [], there are no founder items in this response, so stop this walkthrough without making a person request. If an item lacks a person UUID, stop and inspect its schema rather than substituting another ID.
3. Follow a founder to their person record
Copy a founder UUID from step 2. Set FOUNDER_UUID to that returned value, then request the corresponding entity in the people collection:
FOUNDER_UUID='512f4cc3-76bf-85b6-67cd-4a2ee14f2c75'
curl --request GET \
"https://api.crunchbase.com/v4/data/entities/people/$FOUNDER_UUID?field_ids=identifier,short_description" \
--header "X-cb-user-key: $CRUNCHBASE_API_KEY" \
--header 'Accept: application/json'The assignment above uses the UUID from the preceding excerpt. Replace it with the UUID from your response if different.
Response excerpt; other fields omitted:
{
"properties": {
"identifier": {
"uuid": "512f4cc3-76bf-85b6-67cd-4a2ee14f2c75",
"value": "Michael Arrington",
"permalink": "michael-arrington"
},
"short_description": "Michael Arrington is the Founder and Managing Member of Arrington Capital. He attended Claremont McKenna College."
}
}Read the person’s description at properties.short_description. Descriptions are optional and can change over time.
Check: the response is HTTP 200, and properties.identifier.uuid matches the founder UUID you copied. You have followed a relationship from an organization to a separate person entity.
The collection in the URL changes from organizations to people. The organization UUID and the founder UUID refer to different entities; use the identifier for the entity you want to retrieve.
If the lookup fails: confirm the URL uses people and the UUID came from the founder’s identifier.uuid. A 404 may require re-reading the card because live records can change. If the returned UUID does not match, stop; do not attach that person to the company. A missing description does not invalidate an otherwise matching person identifier.
What you can build from these responses
You now have enough data for a small enrichment result:
| Summary field | Example value | Source |
|---|---|---|
| Company | Crunchbase | Step 1: properties.identifier.value |
| Website | https://www.crunchbase.com | Step 1: properties.website_url |
| Founder | Michael Arrington | Step 3: properties.identifier.value |
| Founder UUID | 512f4cc3-76bf-85b6-67cd-4a2ee14f2c75 | Step 2: cards.founders[0].identifier.uuid; matched in step 3 |
This is a summary you assemble, not a separate API response. For an organization with multiple founders, repeat the person lookup for each returned founder you need. Embedded cards can be partial; use card pagination when you need all available records.
Run the sequence in Python
This optional example makes the same three requests and prints a summary for one returned founder. It requires Python 3 and uses only the standard library. It does not paginate the card or retrieve every founder.
- Save the code below as
company_founder.py. - Set
CRUNCHBASE_API_KEYin the same terminal environment, using your provisioned key. See key retrieval and authentication setup if needed. - Run
python3 company_founder.py(orpythonif that is your Python 3 command).
Show the complete Python example
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode
from urllib.request import Request, urlopen
from uuid import UUID
def lookup(collection, entity_id, **params):
url = (f"https://api.crunchbase.com/v4/data/entities/"
f"{collection}/{quote(entity_id, safe='')}?{urlencode(params)}")
request = Request(url, headers={
"X-cb-user-key": os.environ["CRUNCHBASE_API_KEY"],
"Accept": "application/json",
})
with urlopen(request, timeout=30) as response:
data = json.load(response)
if not isinstance(data, dict):
raise ValueError("Expected a JSON object; stop and inspect the response.")
return data
def properties(data):
props = data.get("properties")
if not isinstance(props, dict) or not isinstance(props.get("identifier"), dict):
raise ValueError("Missing properties.identifier; stop and check the response.")
return props
def main():
if not os.environ.get("CRUNCHBASE_API_KEY", "").strip():
raise ValueError("Set CRUNCHBASE_API_KEY to your provisioned key first.")
company = properties(lookup(
"organizations", "crunchbase",
field_ids="identifier,short_description,website_url"))
related = lookup("organizations", "crunchbase",
field_ids="identifier", card_ids="founders")
cards = related.get("cards")
if not isinstance(cards, dict) or "founders" not in cards:
raise ValueError("Founders card missing; check the request and package access.")
founders = cards["founders"]
if not isinstance(founders, list):
raise ValueError("Expected a founders array; stop and inspect the response.")
if not founders:
raise ValueError("Founders array is empty; no person lookup was attempted.")
# This teaching example follows one returned founder, not the entire card.
first = founders[0]
identifier = first.get("identifier") if isinstance(first, dict) else None
if not isinstance(identifier, dict) or identifier.get("entity_def_id") != "person":
raise ValueError("Expected a person identifier in the founders card.")
founder_uuid = identifier.get("uuid")
if not isinstance(founder_uuid, str):
raise ValueError("Founder UUID missing; do not substitute the company UUID.")
UUID(founder_uuid) # Validate before using the returned value in a URL.
person = properties(lookup("people", founder_uuid,
field_ids="identifier,short_description"))
if person["identifier"].get("uuid") != founder_uuid:
raise ValueError("Person UUID does not match the requested founder UUID.")
print(json.dumps({
"company": company["identifier"].get("value"),
"website": company.get("website_url"),
"founder": person["identifier"].get("value"),
"founder_uuid": founder_uuid,
"founder_description": person.get("short_description"),
}, indent=2))
if __name__ == "__main__":
try:
main()
except HTTPError as error:
print(f"Stopped: HTTP {error.code}. Inspect this status before retrying.", file=sys.stderr)
sys.exit(1)
except (URLError, TimeoutError):
print("Stopped: network error or timeout. Check connectivity before retrying.", file=sys.stderr)
sys.exit(1)
except ValueError as error:
print(f"Stopped: {error}", file=sys.stderr)
sys.exit(1)Successful output looks like this; names and descriptions can change. This is the script’s assembled output, not an API response:
{
"company": "Crunchbase",
"website": "https://www.crunchbase.com",
"founder": "Michael Arrington",
"founder_uuid": "512f4cc3-76bf-85b6-67cd-4a2ee14f2c75",
"founder_description": "Michael Arrington is the Founder and Managing Member of Arrington Capital. He attended Claremont McKenna College."
}Missing optional summary values remain null. Missing or empty founder results stop the example before a person request. The script also stops on HTTP errors and timeouts; it does not retry automatically. For an integration that needs retries, follow the rate-limit and retry guidance.
Keep these terms handy
| Term | Meaning | Example |
|---|---|---|
| Collection | A group of entities of a particular type. | organizations, people, funding_rounds |
| Entity | One record in a collection. | The Crunchbase organization |
| UUID | An entity's stable identifier. Store it to link your records to Crunchbase. | 43ac6c2f-65ec-5b40-89d6-ab9d4c4988ef |
| Permalink | A readable identifier accepted by Entity Lookup in place of a UUID. | crunchbase |
| Field | A named piece of data. Use field_ids to select fields supported by the endpoint. | short_description |
| Card | A named group of additional data associated with an entity, often related records. Use card_ids in Entity Lookup to request it. | founders |
An identifier is itself an object: its uuid identifies the entity, value is the display name, and permalink is its readable identifier. Do not use the display name as a unique key.
Choose the right operation
| What you have or need | Use | What to do with the result |
|---|---|---|
| A name or partial name | Autocomplete | Compare the suggestions and select the intended entity. Autocomplete does not guarantee an exact match. |
| Criteria for finding a set of entities | Search | Inspect matching entities and paginate when needed. |
| A UUID or permalink for one entity | Entity Lookup | Request its fields and cards. |
| More records from one card | Single-card lookup | Paginate the card's results. |
Use the collection and identifiers returned by the API; do not construct them from display names.
Read each value according to its shape
| Value | How to read it |
|---|---|
A string, such as website_url | Read the string directly. |
| An identifier object | Read its uuid to identify the entity and value for display. |
| An array, such as the founders results | Iterate over its items; check whether the array exists and has items before selecting one. |
A structured date, such as founded_on | Read its value and preserve its precision. Other date fields can be strings; check each field’s definition. |
A monetary value, such as funding_total | Keep value and currency together; value_usd is the USD value. Availability of financial fields depends on your package. |
Consult the endpoint response schema in the API reference for exact field types and the Data Dictionary for field definitions.
Treat fields and cards as optional unless the schema explicitly requires them. An omitted field is not the same as zero, false, or evidence that no relationship exists. Check the request, available data, and package before drawing a conclusion. HTTP 200 alone does not confirm that every field you intended to request was returned.
Avoid these common mistakes
- Using a name as an ID: resolve candidates first, then retain the selected UUID.
- Putting a card name in
field_ids: requestfoundersthroughcard_ids; use the endpoint schema to find each supported parameter value. - Treating a card as a complete dataset: an embedded card returns at most 100 items. Use single-card lookup and pagination when you need more.
- Assuming every collection and field is available: use the reference for your licensed package.
- Inferring missing values: preserve missing data as missing rather than filling in business conclusions.
Recover from a failed request
| Result | Next action |
|---|---|
| 401 | Confirm the provisioned key is in X-cb-user-key, without a Bearer prefix or extra spaces. Correct authentication before retrying. |
| 403 | Check the response message and your provisioned access with your account team. Repeating the unchanged request will not fix an access restriction. |
| 404 | Check the collection and entity identifier. For step 3, use the returned person UUID; re-read the founders card if the record may have changed. |
| 429 | Pause and follow the rate-limit and retry guidance, including Retry-After when provided. |
| 5xx or a network timeout | Follow the same bounded retry guidance for transient failures. Stop after the retry limit and report the failure. |
| 200 with missing data | Check the response shape, field/card spelling, and package schema. Missing does not mean zero, false, or no relationship. |
If you need help, provide the endpoint, HTTP status, and a sanitized error message to your account team. Do not include your API key.
Use this workflow in an automated client
- Resolve the entity. Start with a known UUID or permalink, or select a verified match from Autocomplete/Search. Retain its collection and UUID.
- Check the request. Use exact supported field and card names from the reference for your package. Authenticate with the provisioned REST API key.
- Validate the response. Check the HTTP result and expected objects/arrays. An HTTP 200 by itself is insufficient.
- Follow a returned identifier. Confirm the related entity type and UUID before selecting the next collection. Verify that the lookup returns the requested UUID.
- Preserve uncertainty. Keep missing optional values missing. Stop a dependent lookup when the required card or identifier is absent; report the limitation instead of inventing a result.
- State the scope. This example follows one founder. To claim all available results, paginate the card and process the returned records. Follow the canonical pagination guide rather than assuming an embedded card is complete.
An AI agent should use these same checks and stop conditions. The explanatory text includes the full relationship path, so the workflow does not depend on reading the diagram.
Next steps
- Next: Find companies with the Search API: search by industry and headquarters location, then look up a returned company.
- Request additional fields and cards.
- Retrieve more results from a card.
Updated about 1 hour ago

