# Quickstart

> From zero to your first enriched job payload in about two minutes: create a key, make one authenticated request to /v1/jobs, and read what comes back.

Source: https://hyperjobs.io/docs/quickstart

## Before you start

You need an API key. Keys are created in the
[dashboard](/dashboard) and shown exactly once — see
[authentication](/docs/authentication) for the details.

<Steps>
  <Step title="Create an API key">
    Sign in, open the dashboard, and create a key. It looks like
    `hj_live_` followed by 48 hex characters.

    Copy it immediately. Only a hash is stored, so the full key cannot be
    shown again — if you lose it, revoke it and make another.
  </Step>

  <Step title="Check you can reach the API">
    `/v1/health` needs no key, so it isolates network problems from auth
    problems:

    ```bash title="cURL"
    curl https://api.hyperjobs.io/v1/health
    ```

    ```json title="Response"
    { "ok": true, "jobs": 596768, "version": "1.0.0" }
    ```
  </Step>

  <Step title="Make your first authenticated request">
    Ask for one remote job with a salary attached:

    <CodeGroup>
    ```bash title="cURL"
    curl -H "Authorization: Bearer $HYPERJOBS_KEY" \
      "https://api.hyperjobs.io/v1/jobs?remote=1&has_salary=true&limit=1"
    ```

    ```js title="Node"
    const res = await fetch(
      "https://api.hyperjobs.io/v1/jobs?remote=1&has_salary=true&limit=1",
      { headers: { Authorization: `Bearer ${process.env.HYPERJOBS_KEY}` } },
    );
    const { data, total } = await res.json();
    console.log(total, data[0].title, data[0].company.name);
    ```

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

    res = requests.get(
        "https://api.hyperjobs.io/v1/jobs",
        params={"remote": 1, "has_salary": "true", "limit": 1},
        headers={"Authorization": f"Bearer {os.environ['HYPERJOBS_KEY']}"},
        timeout=30,
    )
    res.raise_for_status()
    payload = res.json()
    print(payload["total"], payload["data"][0]["title"])
    ```

    ```go title="Go"
    req, _ := http.NewRequest("GET",
        "https://api.hyperjobs.io/v1/jobs?remote=1&has_salary=true&limit=1", nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("HYPERJOBS_KEY"))

    res, err := http.DefaultClient.Do(req)
    if err != nil { log.Fatal(err) }
    defer res.Body.Close()
    ```
    </CodeGroup>
  </Step>

  <Step title="Read the response">
    ```json
    {
      "data": [
        {
          "id": "ashby:7c1f…",
          "title": "Staff Backend Engineer",
          "company": {
            "slug": "linear",
            "name": "Linear",
            "domain": "linear.app",
            "industry": "Software Development",
            "employee_count": 120
          },
          "location": { "remote": true, "countries": ["United States"], "cities": null },
          "salary": { "annual_min": 190000, "annual_max": 240000, "currency": "USD" },
          "skills": ["typescript", "graphql", "postgresql"],
          "apply_url": "https://jobs.ashbyhq.com/linear/…",
          "posted_at": "2026-07-08T14:02:11.000Z"
        }
      ],
      "total": 21497,
      "limit": 1,
      "offset": 0,
      "next_cursor": "eyJwIjoiMjAyNi0wNy0wOCAxNDowMjoxMSIsImlkIjoiYXNoYnk6N2MxZjJhOTQuLi4ifQ"
    }
    ```

    `total` is the full match count, not the page size; `next_cursor` is how
    you [page](/docs/pagination) to the rest. `company` is already there; there
    is no second request.
  </Step>
</Steps>

## Two things worth knowing immediately

<Warning title="Descriptions are omitted by default">
  `description` is **absent** from the response unless you ask for it with
  `description_format=text` or `html`. Descriptions are multi-kilobyte, and
  paying to move 200 of them when you wanted titles is the most common way to
  make this API feel slow. Ask for them when you need them.
</Warning>

<Warning title="Comma means OR — except for skill">
  `?country=US,Germany` matches jobs in either country: on every multi-value
  filter, a comma-separated list is OR. The one exception is `skill`, where the
  list is AND — `?skill=python,aws` returns jobs tagged **both** Python and
  AWS, because "knows Python or AWS" is rarely what a skills filter means. See
  [filtering](/docs/guides/filtering).
</Warning>

## A more realistic query

Senior remote backend roles at US companies, posted this week, with a salary,
one row per company/title pair:

```bash
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "skill=python" \
  --data-urlencode "seniority=Mid-Senior level" \
  --data-urlencode "work_arrangement=Remote" \
  --data-urlencode "country=US" \
  --data-urlencode "has_salary=true" \
  --data-urlencode "time_frame=7d" \
  --data-urlencode "collapse=true" \
  --data-urlencode "limit=50"
```

`country` takes an ISO-2 code or a full name — `US` and `United States` both
work, and a value that's neither is a `400`, not an empty result. See
[filtering](/docs/guides/filtering).

## Next

<CardGroup cols={2}>
  <Card title="Filtering jobs" icon="Funnel" href="/docs/guides/filtering">
    Every filter, with the exact matching semantics.
  </Card>
  <Card title="Syncing the feed" icon="RefreshCw" href="/docs/guides/feed-sync">
    Keep a local copy fresh without re-crawling the index.
  </Card>
  <Card title="The Job object" icon="Braces" href="/docs/objects/job">
    Every field, its type, and when it's null.
  </Card>
  <Card title="Rate limits" icon="Gauge" href="/docs/rate-limits">
    What your plan allows and how throttling behaves.
  </Card>
</CardGroup>
