---
title: "Employers whose live roles this API returns."
method: GET
path: "/v1/companies"
tags: ["companies"]
---

# Employers whose live roles this API returns.

`GET /v1/companies`

## Request

- Base URL: `https://api.jobopportunitiesapi.org`
- URL: `https://api.jobopportunitiesapi.org/v1/companies`
- Auth: HTTP bearer

Every company here has at least one live listing you can fetch, so
`open_roles` and `GET /v1/jobs?company=<slug>` reconcile. That is the
point of the endpoint: a directory entry advertising roles the API will
not serve is a broken promise.

Further companies (22,138 when measured on 8 Aug 2026) were seen hiring on their own careers page,
under a source we do not redistribute. They carry `own_site_roles` and
`open_roles: 0`, their individual pages resolve normally, and they are
included only with `include_discovered=true` — they are evidence a
company is hiring, not inventory you can buy.

Ordered by `GREATEST(open_roles, own_site_roles) DESC, name, slug`. The
slug makes the ordering total: without it ties could be reordered
between two requests (measured 3 Aug 2026: 18,213 tie groups covering
36,464 of 186,558 companies, 19.5%), so offset paging silently repeated
and skipped rows.

`offset` is capped at 100,000 and an offset beyond that is a **422**, not
a silently clamped 200. Use `cursor` for deep paging — it is lossless and
has no limit.

## Query parameters

- `limit` integer
- `offset` integer
- `cursor` string
- `country` string
- `org_type` string
- `source_type` string
- `has_website` boolean
- `include_discovered` boolean
- `q` string

## Response `200`

A page of companies.

- CompanyPage
  - `data` Company[]
    - `careers_url` string
    - `country` string
    - `first_seen` string
    - `industry` string
    - `logo` string
    - `name` string
    - `open_roles` integer: Roles retrievable from THIS API. Never a number the API cannot honour.
    - `org_type` string
    - `own_site_roles` integer: Vacancies seen on the company own careers page.
    - `sectors` string[]
    - `slug` string
    - `source_types` string[]: `aggregator` and `agency` are reserved and match no rows today; see the source_type enum.
    - `website` string
    - `website_verified` string: When we fetched this domain and found the company own name on it.
  - `has_more` boolean
  - `limit` integer
  - `next_cursor` string, nullable
  - `offset` integer

## Other responses

- `401`: Missing, unknown, revoked or expired key. All four are reported identically.
- `402`: The plan's monthly RECORD allowance is spent. Distinct from 429: a 429 means slow down and retry, a 402 means the licence is used up until the allowance resets. The monthly window is the CALENDAR month in UTC: it resets on the 1st of the next month at 00:00 UTC, and the body gives the exact instant. Do not retry before then. Every metered response carries `X-RateLimit-Records-Remaining` and `X-RateLimit-Records-Reset` (the reset instant, Unix seconds), so this is visible long before it happens.
- `422`: A parameter we will not guess at: an unknown source_type or provider, an unparseable timestamp, a malformed cursor. Note the deliberate exceptions, which are clamps rather than errors: `limit` is capped at the plan maximum (a non-numeric value falls back to the default), and comma-separated lists are truncated to the per-parameter maximum shown on each. Unknown query parameters are ignored.
- `429`: Over your plan's per-minute or per-day limit. `Retry-After` says how long (seconds to the next minute boundary, or to UTC midnight on a daily breach). The body says exactly when the limit resets (`resets_at`, `resets_at_text`) and, when a higher plan exists, links a same-key upgrade (`upgrade_url`).

---

[API](https://skmtc.dev/jobopportunitiesapi/apis/job-opportunities-api.md) · [All operations](https://skmtc.dev/jobopportunitiesapi/apis/job-opportunities-api/llms.txt) · [OpenAPI document](https://skmtc.dev/jobopportunitiesapi/apis/job-opportunities-api/revisions/b5ef9709a0c9?raw)
