# Retrieve a company

> Fetch one company profile by slug — the cheapest path to the full profile when a job or a company search has already handed you the slug.

Source: https://hyperjobs.io/docs/api-reference/companies-retrieve

<EndpointHeader method="GET" path="/v1/companies/{slug}" />

One company profile by `slug`. When you already have a slug — off a job or a
[company search](/docs/api-reference/companies-list) result — this is the
cheapest way to the full profile.

<CodeGroup>
```bash title="cURL"
curl -H "Authorization: Bearer $HYPERJOBS_KEY" \
  "https://api.hyperjobs.io/v1/companies/linear"
```

```js title="Node"
const res = await fetch(`https://api.hyperjobs.io/v1/companies/${encodeURIComponent(slug)}`, {
  headers: { Authorization: `Bearer ${process.env.HYPERJOBS_KEY}` },
});
if (res.status === 404) return null; // no profile with that slug

const company = await res.json(); // the company itself — not { data: … }
```

```python title="Python"
import os, requests
from urllib.parse import quote

res = requests.get(
    f"https://api.hyperjobs.io/v1/companies/{quote(slug, safe='')}",
    headers={"Authorization": f"Bearer {os.environ['HYPERJOBS_KEY']}"},
    timeout=30,
)
company = res.json() if res.status_code == 200 else None
```
</CodeGroup>

## Path parameters

<Fields>
  <ParamField path="slug" type="string" required>
    The company's `slug`, matched **exactly**. No normalisation beyond
    URL-decoding — no case-folding, no fuzzy matching, no lookup by name or
    domain.

    Slugs are LinkedIn-style: usually lower-case and hyphenated, and usually
    recognisable from the company name. "Usually" isn't "always" — see
    [where slugs come from](#where-slugs-come-from).
  </ParamField>
</Fields>

No query parameters. The full profile is always returned; there's nothing
optional to request.

## Where slugs come from

<Warning title="Don't construct a slug from a company name">
  Slugs resemble company names often enough to be dangerous. `Linear` → `linear`
  works; the next one you guess won't, and a wrong guess is an [indistinguishable
  404](#errors) rather than a hint.

  ```js
  // Wrong. Works for maybe half of companies, silently 404s for the rest.
  const slug = name.toLowerCase().replace(/\s+/g, "-");
  ```

  Always read the slug from data you were given — and when you only have a name,
  search for it: [`/v1/companies?q=`](/docs/api-reference/companies-list) does
  substring name matching and returns the real `slug`.
</Warning>

Two reliable sources:

| Source | How |
| --- | --- |
| **A job** | [`job.company.slug`](/docs/objects/job#company) — present on 99.9997% of postings |
| **The search endpoint** | [`/v1/companies`](/docs/api-reference/companies-list) — `?q=` name search, plus industry, HQ country, and size filters |

In practice this endpoint is the second half of a jobs query: filter jobs, take
`company.slug` off a result, fetch the profile.

```js
// The normal flow — the slug comes from the data, never from a string transform.
const { data } = await getJobs({ organization: "linear", limit: 1 });
const slug = data[0]?.company?.slug;
if (slug) {
  const company = await getCompany(slug);
}
```

<Note title="A job's company may have no slug">
  When a posting doesn't resolve to a profile, `job.company` is a [minimal
  fallback](/docs/objects/company#the-three-shapes-on-a-job) with no `slug` — and
  nothing to look up here. Guard it rather than interpolating `undefined` into
  the path, which produces a confusing 404 on `/v1/companies/undefined`.
</Note>

## Response

The company object itself, **not wrapped**:

```json title="Response"
{
  "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"
}
```

<Warning title="This response is not wrapped in `data`">
  [`/v1/companies`](/docs/api-reference/companies-list) returns `{ data: [ … ] }`.
  This endpoint returns the company at the top level, matching
  [`/v1/jobs/{id}`](/docs/api-reference/jobs-retrieve). A shared response handler
  that reaches for `.data` gets `undefined` here.
</Warning>

Every field is documented in [the Company object](/docs/objects/company). Three
worth re-reading before you build on this payload:

- **`hq.country` is ISO-2** (`"US"`), while [a job's
  `location.countries`](/docs/objects/job#location) are full names
  (`["United States"]`). Joining them needs a mapping table.
- **`hq`, `funding`, and `stock` are null** when the underlying data is absent —
  and `stock: null` is not evidence a company is private.
- **`jobs_count` is live** — the company's non-duplicate, non-expired openings
  in the pool right now, recomputed roughly every 15 minutes. `0` means not
  currently hiring, not "no data".

<Note>
  The profile returned here is identical to the one embedded on a job. Fetching
  it is only worth a request when you have a `slug` but no job in hand — if you
  just got the job, you already have the whole company object.
</Note>

## Freshness

[`updated_at`](/docs/objects/company#provenance) is when the **profile** last
refreshed, which has nothing to do with [job
ingestion](/docs/reference/coverage#refresh-cadence). A job posted an hour ago
routinely carries a company profile that's months old. That's expected —
headcount and funding move on their own timescale.

## Errors

| Status | Cause |
| --- | --- |
| `401` | Missing, unknown, or revoked key. |
| `404` | No company with that slug. |
| `429` | [Rate limit](/docs/rate-limits) exceeded. |
| `500` | Server error. Retry with backoff. |

```json title="404"
{ "error": "not found" }
```

A `404` means no profile has that slug — most often a constructed or misremembered
one. It carries no detail, and retrying won't change it: treat it as "no such
company" and move on rather than as an error to back off from. To recover, search
for the name — [`/v1/companies?q=`](/docs/api-reference/companies-list) — or go
back to a [jobs query](/docs/api-reference/jobs-list) and read the real slug off
`company.slug`.

## Related

<CardGroup cols={2}>
  <Card title="The Company object" icon="Braces" href="/docs/objects/company">
    Every field in the response above.
  </Card>
  <Card title="List companies" icon="List" href="/docs/api-reference/companies-list">
    The search endpoint — find a slug by name, industry, country, or size.
  </Card>
  <Card title="Company data" icon="Boxes" href="/docs/guides/companies">
    Firmographics — server-side filters and the embedded-company model.
  </Card>
  <Card title="The Job object" icon="Funnel" href="/docs/objects/job">
    Where `company.slug` comes from.
  </Card>
</CardGroup>
