> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xquik.com/llms.txt
> Use this file to discover all available pages before exploring further.

# X job search API for listings, salary & company

> Search public X job listings by keyword, location, seniority, work type, and company. Each job returns its title, salary, and apply link. 1 credit per job.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-search-jobs-200">
      ```json theme={null}
      {
        "jobs": [
          {
            "id": "1899202395210649861",
            "title": "Engineer",
            "url": "https://x.com/i/jobs/1899202395210649861",
            "applyUrl": "https://xquik.com/example",
            "location": "United States"
          }
        ],
        "has_next_page": true,
        "next_cursor": "DAACCgACGRElMJcAAA"
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-search-jobs-400">
      ```json theme={null}
      {
        "error": "invalid_job_filter",
        "message": "Unknown locationType \"moon\". Use onsite, remote or hybrid."
      }
      ```
    </Tab>

    <Tab title="401" id="response-x-search-jobs-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required."
      }
      ```
    </Tab>

    <Tab title="402" id="response-x-search-jobs-402">
      ```json theme={null}
      {
        "error": "insufficient_credits",
        "message": "Insufficient credits. Top up or subscribe to continue."
      }
      ```
    </Tab>

    <Tab title="424" id="response-x-search-jobs-424">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </Tab>

    <Tab title="429" id="response-x-search-jobs-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>

    <Tab title="502" id="response-x-search-jobs-502">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-search-jobs-503">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "Xquik is busy right now. Retry shortly."
      }
      ```
    </Tab>
  </Tabs>
</Panel>

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

Search the public job listings on X by keyword. Narrow them by location, work type, seniority, employment type, or company.

<Note>
  Requested result counts are upper bounds for paid authenticated calls. When remaining credits cannot cover the full page, Xquik returns fewer results. If zero paid results are affordable, it returns `402 insufficient_credits`.
</Note>

<Callout icon="coins" color="#5c3327">
  **1 credit per job returned** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://xquik.com/api/v1/x/jobs/search?q=engineer&locationType=remote&seniority=senior" \
    -H "x-api-key: xq_your_api_key_here" | jq
  ```

  ```javascript Node.js theme={null}
  const query = "engineer";
  const params = new URLSearchParams({
    q: query,
    locationType: "remote",
    seniority: "senior,lead",
  });
  const response = await fetch(`https://xquik.com/api/v1/x/jobs/search?${params}`, {
    headers: { "x-api-key": "xq_your_api_key_here" },
  });
  const data = await response.json();
  const nextCursor = data.has_next_page ? data.next_cursor : null;
  const jobRows = data.jobs.map((job) => ({
    search_query: query,
    job_id: job.id,
    title: job.title ?? null,
    job_url: job.url,
    apply_url: job.applyUrl ?? null,
    location: job.location ?? null,
    salary: job.formattedSalary ?? null,
    employer: job.company?.name ?? job.account?.name ?? null,
    next_cursor: nextCursor,
  }));
  ```

  ```python Python theme={null}
  import requests

  query = "engineer"
  response = requests.get(
      "https://xquik.com/api/v1/x/jobs/search",
      params={"q": query, "locationType": "remote", "seniority": "senior,lead"},
      headers={"x-api-key": "xq_your_api_key_here"},
  )
  data = response.json()
  next_cursor = data["next_cursor"] if data["has_next_page"] else None
  job_rows = [
      {
          "search_query": query,
          "job_id": job["id"],
          "title": job.get("title"),
          "job_url": job["url"],
          "apply_url": job.get("applyUrl"),
          "location": job.get("location"),
          "salary": job.get("formattedSalary"),
          "employer": (job.get("company") or job.get("account") or {}).get("name"),
          "next_cursor": next_cursor,
      }
      for job in data["jobs"]
  ]
  ```
</CodeGroup>

The Node.js and Python snippets build 1 row per job. Store the rows with the cursor before you request the next page.

## Narrow a job search

Start with a keyword in `q`, such as a job title or a skill. Add 1 filter at a time and check the results after each change.

* `location` takes a city, region, or country as text.
* `locationId` takes an exact place from [job locations](/api-reference/x/job-locations).
* `locationType` keeps onsite, remote, or hybrid jobs.
* `seniority` keeps levels from intern to executive.
* `employmentType` keeps full-time, part-time, or contract jobs.
* `company` keeps the jobs of 1 employer.

The 3 list filters take comma lists. They ignore case and accept spaces, hyphens, or underscores between words. `Full Time`, `full-time`, and `full_time` all work.

An unknown value returns `400 invalid_job_filter`. Its message names the values the filter takes.

## Page through job listings

Each page returns `has_next_page` and `next_cursor`. Send `next_cursor` back as `cursor` with the same query and filters. Stop when `has_next_page` is `false`.

A cursor X refuses or that expired returns `400 invalid_cursor`, which is free. Start again without `cursor`, then use `next_cursor`.

A job that closed is left out of the page and costs nothing. A page can therefore hold fewer jobs than `pageSize`.

Use the job `id` as the key in your store. Overlapping searches then add no duplicates.

## Get the full description

Search returns the listing summary. Send a job's `id` to [get 1 job listing](/api-reference/x/get-job) for the full description, work type, seniority, and team.

## Query parameters

<ParamField query="q" type="string" required>
  Keyword, such as a job title or skill. Alias: `query`.
</ParamField>

<ParamField query="location" type="string">
  City, region, or country as text, such as `Berlin`.
</ParamField>

<ParamField query="locationId" type="string">
  Location ID from [job locations](/api-reference/x/job-locations). It replaces `location` when you send both.
</ParamField>

<ParamField query="locationType" type="string">
  Where the work happens. Comma list of `onsite`, `remote`, and `hybrid`.
</ParamField>

<ParamField query="seniority" type="string">
  Comma list of `intern`, `entry_level`, `junior`, `mid_level`, `senior`, `lead`, `manager`, and `executive`.
</ParamField>

<ParamField query="employmentType" type="string">
  Comma list of `full_time`, `full_time_contract`, `part_time`, and `contract_to_hire`.
</ParamField>

<ParamField query="company" type="string">
  Company name to match.
</ParamField>

<ParamField query="cursor" type="string">
  Pagination cursor from a previous response. Omit for the first page.
</ParamField>

<ParamField query="pageSize" type="integer">
  Jobs per page. Range: `1-100`. Defaults to `20`. Aliases: `limit`, `count`, `max_results`, `maxItems`, `max_items` & `per_page`.
</ParamField>

## Headers

<ParamField header="x-api-key" type="string">
  Full account key. Sessions and OAuth also work.
</ParamField>

<ParamField header="Authorization" type="string">
  `Bearer xq_your_guest_key_here` for `paid_reads`.
</ParamField>

## Response

### 200 OK

X leaves out a field that a job lacks. Only `id` and `url` are always present.

<ResponseField name="jobs" type="object[]">
  Array of matching job listings.
  **Job object fields.**

  <ResponseField name="id" type="string">
    Job ID.
  </ResponseField>

  <ResponseField name="title" type="string">
    Job title.
  </ResponseField>

  <ResponseField name="url" type="string">
    The job page on X.
  </ResponseField>

  <ResponseField name="applyUrl" type="string">
    Where applicants apply, off X.
  </ResponseField>

  <ResponseField name="location" type="string">
    Location as the employer wrote it.
  </ResponseField>

  <ResponseField name="formattedSalary" type="string">
    Salary as X shows it, such as `Starting at $225K`.
  </ResponseField>

  <ResponseField name="salaryMin" type="number">
    Lowest salary, in `salaryCurrencyCode` per `salaryInterval`.
  </ResponseField>

  <ResponseField name="salaryMax" type="number">
    Highest salary, in `salaryCurrencyCode` per `salaryInterval`.
  </ResponseField>

  <ResponseField name="salaryCurrencyCode" type="string">
    ISO 4217 currency code of the salary.
  </ResponseField>

  <ResponseField name="salaryInterval" type="string">
    Pay period of the salary: `annually` or `hourly`.
  </ResponseField>

  <ResponseField name="company" type="object">
    Company profile with `id`, `name`, and `logoUrl`. Present on jobs X imported from a job board.
  </ResponseField>

  <ResponseField name="account" type="object">
    The X account that posted the job, with `id`, `username`, `name`, `verified`, `verifiedType`,
    `profilePicture`, and `profileImageShape`. A job has none when X imported it from a job board or
    no longer shows the account.
  </ResponseField>
</ResponseField>

<ResponseField name="has_next_page" type="boolean">Whether more results are available.</ResponseField>
<ResponseField name="next_cursor" type="string">Cursor for the next page.</ResponseField>

```json theme={null}
{
  "jobs": [
    {
      "id": "1899202395210649861",
      "title": "Engineer",
      "url": "https://x.com/i/jobs/1899202395210649861",
      "applyUrl": "https://xquik.com/example",
      "location": "United States",
      "formattedSalary": "Starting at $225K",
      "salaryMin": 225000,
      "salaryCurrencyCode": "USD",
      "salaryInterval": "annually",
      "account": {
        "id": "1812126202804711425",
        "username": "commonwarexyz",
        "name": "commonware",
        "verified": true,
        "verifiedType": "Business"
      }
    }
  ],
  "has_next_page": true,
  "next_cursor": "DAACCgACGE..."
}
```

### 400 Missing query, unknown filter, or invalid cursor

```json theme={null}
{
  "error": "invalid_job_filter",
  "message": "Unknown locationType \"moon\". Use onsite, remote or hybrid."
}
```

A request without `q` returns `missing_query`. A cursor X cannot read returns `invalid_cursor`:

```json theme={null}
{
  "error": "invalid_cursor",
  "message": "Cursor invalid or expired. Start again without cursor, then use next_cursor."
}
```

Start again without `cursor`. All 3 answers are free.

### 401 Unauthenticated

Anonymous requests get `WWW-Authenticate: Bearer` and a guest wallet checkout action. This is not a Payment challenge.

```json theme={null}
{ "error": "unauthenticated" }
```

### 402 Payment required

Account keys get account options. Guest keys get guest top-up only.
No checkout starts automatically. Confirm any payment action.

### 502 X API unavailable

```json theme={null}
{ "error": "x_api_unavailable" }
```

The read service returned an error. Retry after a short delay.

### 429 Rate limit exceeded

```json theme={null}
{ "error": "rate_limit_exceeded", "retryAfter": 60 }
```

You exceeded your tier rate limit. Wait for the `Retry-After` header before retrying.

### 424 Dependency failed

```json theme={null}
{ "error": "x_api_unavailable" }
```

The normalized v1 response contract can return 424 when the read service is unavailable.

<Note>
  **Next steps.** [Get 1 job listing](/api-reference/x/get-job) for the full description, or [search users](/api-reference/x/search-users) to find the hiring accounts.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.