- API reference
- Objects
The Company object
Every field on a company profile, its type, when it's null, and how the embedded version on a job differs from the full one.
The shape returned by
/v1/companies/{slug} and
/v1/companies, and embedded as
company on every job.
158,545 companies have a profile. Coverage within a profile is uneven — name
and slug are dependable, funding and stock exist for a small minority — so
treat every field below as optional.
Example#
{
"slug": "linear",
"name": "Linear",
"domain": "linear.app",
"website": "https://linear.app",
"logo": "https://media.licdn.com/dms/image/…",
"industry": "Software Development",
"type": "Privately Held",
"description": "Linear is a purpose-built tool for planning and building products…",
"slogan": "The issue tracking tool you'll enjoy using",
"specialties": ["issue tracking", "project management", "developer tools"],
"employee_count": 120,
"size_range": "51-200",
"followers": 48210,
"founded_year": 2019,
"hq": {
"country": "US",
"region": "California",
"locality": "San Francisco",
"raw": "San Francisco, California, United States"
},
"funding": {
"rounds": 4,
"last_round_type": "Series B",
"last_round_date": "2022-06-15",
"last_round_amount_usd": 35000000,
"total_investment_usd": 52000000,
"crunchbase_slug": "linear-2",
"crunchbase_categories": ["Software", "Productivity Tools"]
},
"stock": null,
"affiliated_companies": [],
"data_tier": "remco",
"jobs_count": 12,
"updated_at": "2026-07-02T09:14:33.000Z"
}The three shapes on a job#
Before the field list, the distinction that causes the most confusion:
job.company is not always this object.
| Shape | When | How to detect |
|---|---|---|
Full PublicCompany | The posting resolved to a profile. 99.9997% of jobs. | company.slug is present |
| Minimal fallback | No profile matched. Six fields, built from what the posting itself declared. | company is non-null, company.slug is absent |
null | The posting carries no employer name at all. | company === null |
The fallback carries only name, domain, website, logo, industry,
description. It's structurally a subset of the full object, which is exactly
what makes it easy to miss — the code path that reads company.name works fine
and the one that reads company.employee_count quietly gets undefined.
Detect the fallback with `slug`, not with field presence
slug is the only field guaranteed on a full profile and impossible on a
fallback. Every other candidate — industry, logo, description — is
nullable on the full object too, so its absence proves nothing.
const isEnriched = job.company?.slug != null;
// Fine on both shapes.
const name = job.company?.name ?? "Unknown";
// Only meaningful when enriched — undefined on a fallback, and `undefined > 500`
// is false, so a filter like this silently drops fallback companies.
if (isEnriched && job.company.employee_count > 500) { /* … */ }A fallback has no slug, so there's nothing to fetch from
/v1/companies/{slug}. Guard the
lookup — building a URL with undefined in it earns a
404.
The fallback is rare (roughly 2 jobs in 600,000 fail to resolve), but "rare" isn't "never", and a crash on the unenriched path is the kind of bug that surfaces in production weeks later.
Identity#
slugThe company's identifier and primary key — linear, stripe. This is what
/v1/companies/{slug} takes, and
what job.company.slug gives you.
Matched exactly. It's a LinkedIn-style slug, so it usually resembles the
company name, but don't construct one from a name and expect it to
resolve — read it from a job or from
/v1/companies.
nameDisplay name.
domainPrimary domain, e.g. linear.app. Bare hostname, no scheme.
websiteFull website URL, including scheme. Related to domain but sourced
separately — the two can disagree, and either can be null while the other
isn't.
logoLogo image URL. Hosted on the upstream provider's CDN, not by us, so it can rot independently of the profile. Fall back gracefully when it 404s.
Profile#
industryIndustry label, e.g. Software Development. A large open vocabulary rather
than a fixed enum, and not the same vocabulary as a job's
taxonomies.
typeOrganisation type as published — e.g. Privately Held. Free-form; treat it
as display text.
descriptionCompany description, plain text. Can run to several paragraphs.
sloganTagline, when the company publishes one.
specialtiesSelf-declared specialties. An array — empty rather than null when there are none.
affiliated_companiesNames of related companies — subsidiaries, parents, regional entities. An array, empty when there are none. These are names, not slugs, so they aren't directly fetchable.
Size and age#
employee_countReported headcount. A point-in-time figure from the last profile refresh, not a live number.
size_rangeHeadcount bracket as published, e.g. 51-200. Can be present when
employee_count is null, and the two are sourced independently — an
employee_count outside its own size_range is unusual but possible.
followersSocial follower count. The sort key for
/v1/companies.
founded_yearYear founded, as a number — 2019, not "2019" and not a date.
HQ#
hqHeadquarters location. null when all four fields are null.
HQ country is ISO-2, job countries are not#
Two country vocabularies, one dataset
The same country is spelled two different ways depending on which object you're holding:
| Field | Value | Vocabulary |
|---|---|---|
company.hq.country | "US" | ISO-2 code |
job.location.countries | ["United States"] | Full names |
This is a real inconsistency, not a documentation simplification. Two consequences:
- Joining requires a mapping.
job.location.countries.includes(company.hq.country)is always false. You need an ISO-2 → name table between them. - The filters absorb it, the data doesn't.
?country=accepts full names or ISO-2 on both/v1/jobsand/v1/companies— but the jobs filter searches the job's location and the companies filter searches HQ, and the field values keep their separate vocabularies.
A company's HQ is also not where its jobs are — a US-headquartered company
posts roles worldwide. Filter jobs by the job's location; use hq to describe
the employer, not to locate the work.
Funding and stock#
fundingVenture funding history. null when all fields are null — which is the
common case, since most companies in the dataset have no venture funding to
report.
stockPublic listing. null for private companies and for public ones whose
listing we don't have — the absence of stock is not evidence a company is
private.
Both money fields are USD regardless of where the round was raised, which makes them the only normalised currency in the API — job salaries keep their original currency.
Provenance#
data_tierWhere the profile's data came from, which is a rough proxy for how complete and how fresh it is. See below.
jobs_countThe company's live openings — non-duplicate, non-expired postings in the
pool right now. Present on list and
retrieve — not on the company
embedded on a job. Recomputed roughly every 15 minutes; 0 means not
currently hiring, not "no data". To filter on it, use has_jobs=true on
/v1/companies.
updated_atWhen the profile was last refreshed, ISO 8601 UTC.
Company profiles refresh on their own cadence, unrelated to job ingestion. A job posted an hour ago can carry a company profile that's months old — that's expected, not a bug.
data_tier values#
The tiers you'll care about rank like this:
| Tier | Companies | Meaning |
|---|---|---|
remco | 12,392 | Richest profiles. Sorted first by /v1/companies. |
linkedin | 67,357 | Standard enriched profile. |
free | 75,880 | Thinnest profiles. The largest tier. |
Ten values exist, not three
Those three cover about 98% of companies, but the field carries ten distinct values in live data. The rest describe how a company was matched to its web presence:
| Tier | Companies |
|---|---|
free | 75,880 |
linkedin | 67,357 |
remco | 12,392 |
bidirectional | 1,566 |
sibling | 467 |
site-declared | 349 |
linkedin-fresh | 260 |
bidirectional-variant | 122 |
site-declared-mismatch | 107 |
site-declared-noweb | 45 |
| Total | 158,545 |
Treat data_tier as an open vocabulary. Code that switches on three values
and throws on a fourth will meet sibling eventually — fall through to a
default instead. New tiers appear as matching strategies are added, and they
arrive without a version bump.
Firmographic filtering#
Server-side, from two directions:
- Search companies directly.
/v1/companiestakesq(name substring),industry,country(HQ),size,employee_count_gte/lt, andhas_jobs=true, with a realtotalandoffsetpaging. "Find all fintech companies in Germany with 200+ employees" is now one query:?industry=Financial Services&country=DE&employee_count_gte=200. - Filter jobs on company attributes.
/v1/jobshas the company axis too —company_industry,company_size,employee_count_gte/lt,funding_gte/lt,company_country,domain— for when the question is really about the postings at companies like that.
For anything not covered by a filter (founded_year, followers, stock),
the embedded company on every job still makes client-side predicates free —
company data covers all three routes.
Nullability at a glance#
| Field | Null when |
|---|---|
slug | Never on a full profile; absent on a job's fallback company |
jobs_count | Never — 0 when there are no live openings; absent on a job's embedded company |
specialties, affiliated_companies | Never — empty arrays instead of null |
hq, funding, stock | All of the object's own fields are null |
| Everything else | Whenever the upstream profile didn't carry it |
Note the split: the two array fields go empty, the three nested objects go null.
company.specialties.length === 0 is safe; company.funding.rounds is not.
This differs from job.location,
which never collapses because one of its fields always resolves. On a company,
the nested objects really do become null — always guard them.
Related#
Fetch one profile by slug.
Search the directory — name, industry, HQ country, size, hiring status.
Company dataFirmographics — server-side filters and the embedded-company model.
The Job objectWhere company shows up embedded, and in which shapes.