# List companies

> Search company profiles by name, industry, HQ country, size, headcount, and hiring status.

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

<EndpointHeader method="GET" path="/v1/companies" />

Searches company profiles. Filter by name substring, industry, HQ country, size
bucket, headcount range, and whether the company has a live job — with a real
`total` and `offset` paging. (An early draft of this API made this a browse
endpoint with a single `limit` parameter and no way past its top 200; that
limitation never shipped.)

<CodeGroup>
```bash title="cURL"
curl -H "Authorization: Bearer $HYPERJOBS_KEY" \
  "https://api.hyperjobs.io/v1/companies?industry=Software%20Development&country=DE&has_jobs=true"
```

```js title="Node"
const qs = new URLSearchParams({
  industry: "Software Development",
  country: "DE",
  has_jobs: "true",
});
const res = await fetch(`https://api.hyperjobs.io/v1/companies?${qs}`, {
  headers: { Authorization: `Bearer ${process.env.HYPERJOBS_KEY}` },
});
const { data, total } = await res.json(); // { data, total, limit, offset }
```

```python title="Python"
import os, requests

res = requests.get(
    "https://api.hyperjobs.io/v1/companies",
    params={"industry": "Software Development", "country": "DE", "has_jobs": "true"},
    headers={"Authorization": f"Bearer {os.environ['HYPERJOBS_KEY']}"},
    timeout=30,
)
res.raise_for_status()
body = res.json()  # { data, total, limit, offset }
```
</CodeGroup>

## Query parameters

<Fields>
  <ParamField query="q" type="string">
    Case-insensitive substring match on company name. `q=data` matches
    "Databricks" and "Big Data GmbH" alike.
  </ParamField>
  <ParamField query="industry" type="string">
    LinkedIn industry (exact, case-insensitive), comma = OR. An open vocabulary,
    not an enum — read values off
    [`company.industry`](/docs/objects/company#profile) rather than guessing.
  </ParamField>
  <ParamField query="country" type="string">
    HQ country — full name or ISO-2, comma = OR. Filters
    [`hq.country`](/docs/objects/company#hq), which stores ISO-2 codes, so
    `country=US,DE` is the dependable form.
  </ParamField>
  <ParamField query="size" type="string">
    Size bucket, comma = OR — e.g. `11-50 employees`. Matches
    [`size_range`](/docs/objects/company#size-and-age) exactly.
  </ParamField>
  <ParamField query="employee_count_gte" type="integer">
    Minimum employee count (inclusive).
  </ParamField>
  <ParamField query="employee_count_lt" type="integer">
    Maximum employee count (exclusive).
  </ParamField>
  <ParamField query="has_jobs" type="boolean">
    `true` → only companies with at least one live job in the pool.
  </ParamField>
  <ParamField query="limit" type="integer" default="50">
    Results per page (default 50, max 200, min 1). An out-of-range or
    non-numeric value is a [`400`](/docs/errors) — nothing is clamped or
    coerced anymore.
  </ParamField>
  <ParamField query="offset" type="integer" default="0">
    Results to skip. Max 100000.
  </ParamField>
</Fields>

Unknown parameters are rejected with a `400` that names the parameter — a
typo'd filter can no longer masquerade as an empty (or unfiltered) result.

## Response

`{ data, total, limit, offset }`. `total` is the full match count across the
dataset, not the page size, and every company carries `jobs_count` — its live
(non-duplicate, non-expired) openings.

```json title="Response"
{
  "data": [
    {
      "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", "…": "…" },
      "stock": null,
      "affiliated_companies": [],
      "data_tier": "remco",
      "jobs_count": 12,
      "updated_at": "2026-07-02T09:14:33.000Z"
    }
  ],
  "total": 158545,
  "limit": 50,
  "offset": 0
}
```

Each element is a full [Company object](/docs/objects/company). There is no
`next_cursor` here — companies page by `offset` (max 100000), unlike
[`/v1/jobs`](/docs/api-reference/jobs-list), which also offers
[cursor pagination](/docs/pagination).

## Companies through jobs

The other direction works too. [`/v1/jobs`](/docs/api-reference/jobs-list) has
company-axis filters — `company_industry`, `company_size`,
`employee_count_gte`/`lt`, `funding_gte`/`lt`, `company_country`, `domain` — so
"remote Rust roles at sub-500-person companies" is one jobs query, no company
call involved. Use those when the question is about jobs; use this endpoint
when the question is about the companies themselves. [Company
data](/docs/guides/companies) walks through both routes.

## Sort order

Fixed, not configurable, and applied after your filters:

1. **[`data_tier`](/docs/objects/company#data_tier-values) `remco` first.** The
   richest, verified-tier profiles sort ahead of everything else.
2. **Then `followers`, descending.** Best-known companies first within each
   group.

A default unfiltered `limit=50` still returns 50 heavily-followed `remco`
companies — but with filters and `offset` you can now reach any of the 158,545,
not just the well-known head.

## Errors

| Status | Cause |
| --- | --- |
| `400` | Unknown parameter or invalid value. The body names the `param` and carries a `hint`. |
| `401` | Missing, unknown, or revoked key. |
| `429` | [Rate limit](/docs/rate-limits) exceeded, or monthly quota spent. |
| `500` | Server error. Retry with backoff. |

Validation is strict: every parameter you send must exist and parse. That's a
feature — a misspelled filter is an immediate `400` telling you what to fix,
never silently dropped and returning unfiltered results.

## Related

<CardGroup cols={2}>
  <Card title="Retrieve a company" icon="Building2" href="/docs/api-reference/companies-retrieve">
    Fetch one profile by `slug`.
  </Card>
  <Card title="The Company object" icon="Braces" href="/docs/objects/company">
    Every field in the response above.
  </Card>
  <Card title="Company data" icon="Boxes" href="/docs/guides/companies">
    Firmographics — server-side filters and the embedded-company model.
  </Card>
  <Card title="List jobs" icon="Funnel" href="/docs/api-reference/jobs-list">
    The jobs search, including its company-axis filters.
  </Card>
</CardGroup>
