# Search job postings with OpenWeb Ninja's JSearch API

OpenWeb Ninja's JSearch API returns live job postings from Google for Jobs, which covers LinkedIn, Indeed, Glassdoor, ZipRecruiter and other public job sites, plus job details, apply links and salary estimates, all as JSON. Each call is one request at $0.03 per 10 requests, drawn from a plan that your credential holds. Send `GET https://agents.openwebninja.com/zcj/14l8a102vp2u/jsearch/search-v2` with `query`, `country`, `num_pages=1` and `Authorization: Bearer <token>`, and add `fields` to keep only the job fields you read.

## At a glance

- **Endpoints:** `GET /jsearch/search-v2` (search), `GET /jsearch/job-details` (one posting), `GET /jsearch/estimated-salary` (pay by title and location), `GET /jsearch/company-job-salary` (pay at one employer)
- **Search parameters:** `query` (required, job title and location), `country`, `num_pages=1`, `cursor`, `fields`, and the filters `date_posted`, `work_from_home`, `employment_types`, `job_requirements`
- **Price:** $0.03 per 10 requests. Each call is one request, so 1,000 single-page calls cost $3.
- **Payment:** there is no per-call payment. Each call draws on a plan held by the credential in `Authorization`: Free ($0.15 of credit, needs a credential claimed with a verified email) or Credits (prepaid, minimum $0.50, $0.50 by card).
- **Response:** `data.jobs` (up to 10 postings per page) and `data.cursor` for the next page
- **Access:** `Authorization: Bearer <token>`. No upstream API key is needed.
- **Full purchase flow:** [the buyer skill](https://agents.openwebninja.com/zcj/14l8a102vp2u/SKILL.md)

## Search job postings and open one

1. Search with `fields` set to the job fields you read:

   ```bash
   curl -s "https://agents.openwebninja.com/zcj/14l8a102vp2u/jsearch/search-v2?query=data+engineer+in+Austin,+TX&country=us&num_pages=1&fields=job_id,job_title,employer_name,job_publisher,job_employment_type,job_city,job_state,job_is_remote,job_posted_at_datetime_utc,job_min_salary,job_max_salary,job_apply_link" \
     -H "Authorization: Bearer $ACCESS_TOKEN"
   ```

   This example returned 10 jobs and a cursor, and drew $0.003 from credit. The first job in the response looks like this:

   ```json
   {
     "status": "OK",
     "parameters": {"query": "data engineer in Austin, TX", "num_pages": 1, "country": "us", "language": "en"},
     "data": {
       "jobs": [
         {
           "job_id": "<job_id>",
           "job_title": "AI Data Engineering Manager - GPSC Insights Engineering",
           "employer_name": "General Motors",
           "job_publisher": "General Motors Careers",
           "job_employment_type": "Full-time",
           "job_apply_link": "https://search-careers.gm.com/[path]",
           "job_is_remote": false,
           "job_posted_at_datetime_utc": "2026-11-03T07:00:00.000Z",
           "job_city": null,
           "job_state": null,
           "job_min_salary": null,
           "job_max_salary": null
         }
       ],
       "cursor": "<cursor>"
     }
   }
   ```

   Fields such as `job_city` and `job_min_salary` are nullable, so handle `null`. Put both the title and the location in `query`, and set `country` for jobs outside the US.

2. Leave out `fields` only when you need every field. With `fields`, the example response was about 9 KB. The same query without `fields` returned about 83 KB, with the full description, apply options, logo and Google link for each job. The charge stays the same: the search without `fields` also drew $0.003.

3. Fetch the next page by passing `data.cursor` back as `cursor`, with the same query. Keep `num_pages=1`. When `data.cursor` is `null`, you have reached the last page.

   ```bash
   curl -s -G "https://agents.openwebninja.com/zcj/14l8a102vp2u/jsearch/search-v2" \
     --data-urlencode "query=data engineer in Austin, TX" \
     --data-urlencode "country=us" --data-urlencode "num_pages=1" \
     --data-urlencode "cursor=<cursor>" \
     -H "Authorization: Bearer $ACCESS_TOKEN"
   ```

4. Open one posting by its `job_id`. URL-encode the id, because ids can contain `=`:

   ```bash
   curl -s -G "https://agents.openwebninja.com/zcj/14l8a102vp2u/jsearch/job-details" \
     --data-urlencode "job_id=<job_id>" --data-urlencode "country=us" \
     --data-urlencode "fields=job_id,job_title,employer_name,job_highlights,apply_options" \
     -H "Authorization: Bearer $ACCESS_TOKEN"
   ```

   The response's `data` is an array holding one posting. In this example, `apply_options` listed 4 boards, each with `apply_link`, `is_direct` and `publisher`, and `job_highlights` held `Qualifications` and `Responsibilities` as arrays of bullet strings. The call drew $0.003.

## Narrow the search

| To keep | Parameter and value |
| --- | --- |
| Recent postings | `date_posted` = `today`, `3days`, `week` or `month` (default `all`) |
| Remote jobs only | `work_from_home=true` |
| One or more employment types | `employment_types` = comma list of `FULLTIME`, `CONTRACTOR`, `PARTTIME`, `INTERN` |
| Experience or degree requirements | `job_requirements` = comma list of `under_3_years_experience`, `more_than_3_years_experience`, `no_experience`, `no_degree` |
| Postings from other publishers | `exclude_job_publishers` = comma list of publishers |

`radius` (in km) is passed to Google for Jobs, which may not follow it strictly.

## Get a salary estimate

Send `job_title` and `location` to the estimated-salary endpoint:

```bash
curl -s "https://agents.openwebninja.com/zcj/14l8a102vp2u/jsearch/estimated-salary?job_title=data+engineer&location=Austin,+TX" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

This example returned one estimate and drew $0.003. Its main fields were these:

```json
{"location": "Austin, TX", "job_title": "Data Engineer", "min_salary": 114934.59, "max_salary": 178422.75, "median_salary": 142396.42, "salary_period": "YEAR", "salary_currency": "USD", "salary_count": 911, "publisher_name": "Glassdoor", "confidence": "CONFIDENT"}
```

`salary_count` is the number of salaries behind the estimate. The response also splits pay into base and additional pay. You can narrow the estimate with `location_type` (`ANY`, `CITY`, `STATE`, `COUNTRY`) and `years_of_experience` (`ALL`, `LESS_THAN_ONE` through `ABOVE_FIFTEEN`).

For pay at one employer, send `company` and `job_title` to `GET https://agents.openwebninja.com/zcj/14l8a102vp2u/jsearch/company-job-salary`, for example `?company=Amazon&job_title=software+developer`. It returns the same salary fields plus `company`. When no data exists, it returns an empty `data` array.

## Access and charges

- If a call has no credential, it returns `401 bearer_required`. If your credential holds no covering plan, it returns `403 plan_required`. Neither quotes a per-call price.
- Plans are bought with `POST /plans/{planId}/purchase`, with plan ids in [the catalog](https://agents.openwebninja.com/zcj/14l8a102vp2u/manifest.json); [the buyer skill](https://agents.openwebninja.com/zcj/14l8a102vp2u/SKILL.md) has the purchase steps. A 402 you have not paid charges nothing.
- Each delivered call reports what your balance paid in the `zc-billing` header's `chargedUsd`.
- Reading the catalog and the API reference is free and needs no credential.

## Errors

| Response | Meaning | Action |
| --- | --- | --- |
| `401 bearer_required` | No agent credential | Register one at [auth.md](https://agents.openwebninja.com/zcj/14l8a102vp2u/auth.md), then retry with the bearer |
| `401 invalid_token` | The token expired or did not verify | Exchange the stored assertion for a fresh token |
| `403 plan_required` | No plan covers JSearch | Buy a plan listed in the body, then retry |
| `403 verified_email_required` | Free needs a claimed credential | Complete the claim, or buy Credits |
| `usage_exhausted` | Plan credit is used up | Top up with `POST https://agents.openwebninja.com/zcj/14l8a102vp2u/extend` and `{"amountUsd": "..."}` |
| `503` | A temporary error. It is not billed. | Retry with bounded backoff |

## Frequently asked questions

### Is JSearch a Google for Jobs API?

Yes. JSearch searches Google for Jobs, so one query returns postings from many public job sites. The `job_publisher` field names the site that carried each posting.

### How much does the JSearch API cost?

Every JSearch call is one request at $0.03 per 10 requests, whether it is a search, a job detail or a salary estimate. The example search drew $0.003 with `fields`. The same search without `fields` drew $0.003. A thousand single-page calls cost $3, drawn from Credits (minimum $0.50) or from the Free plan's $0.15.

### Does the JSearch API need an API key?

It needs no upstream API key. Each call carries a bearer token from a free agent credential that holds a plan at the storefront, and [the buyer skill](https://agents.openwebninja.com/zcj/14l8a102vp2u/SKILL.md) covers registering and buying.

### How do I get more than 10 job postings?

Pass `data.cursor` from the previous response as `cursor` on the next request, with the same query and `num_pages=1`. Each page is one call and holds up to 10 postings. Stop when `data.cursor` is `null`.

### Can JSearch return job salary data?

Yes. `estimated-salary` returns the minimum, maximum and median pay for a job title in a location, with `salary_count` and `confidence`. `company-job-salary` returns the same estimate for one company.

## Keep reading

- [Buyer skill](https://agents.openwebninja.com/zcj/14l8a102vp2u/SKILL.md): the full purchase flow
- [Identity](https://agents.openwebninja.com/zcj/14l8a102vp2u/auth.md): register and claim an agent credential
- [How to pay](https://agents.openwebninja.com/zcj/14l8a102vp2u/payment.md): plans, rails and top-ups
- [Error reference](https://agents.openwebninja.com/zcj/14l8a102vp2u/errors.md): every storefront error code
- [JSearch API reference](https://agents.openwebninja.com/zcj/14l8a102vp2u/services/jsearch/openapi.json): every parameter and response field

---

Updated 2026-11-05. All OpenWeb Ninja guides: https://agents.openwebninja.com/guides.md. Storefront: https://agents.openwebninja.com/zcj/14l8a102vp2u/llms.txt.
