# Search jobs

Live LinkedIn job postings by company, keyword, and filters.

**POST** `https://api.up2data.ai/v1/search/jobs`

**Credits:** 1 credit per job returned - **Mode:** synchronous

Provide at least one of `company_url`, `keywords`, or `filters`; a request with none of them (or only empty values) returns an empty, unbilled result that still counts against the search quota. Several `keywords` match any of them. Results come most recent first. Without `filters.locations` the search is worldwide; each location is resolved to LinkedIn's closest geo match. `description_snippet` is filled for as many jobs as the live call has time for and omitted for the rest; use [job enrich](https://up2data.ai/docs/v1/api/jobs-enrich/) for the full posting. `meta.reason` is `success`, or `empty_result` when nothing matched.

> **Info:** Counts as 1 request against the 300/min per-key limit and 1 search against the 20/hour per-organization search quota. See [Rate limits](https://up2data.ai/docs/v1/guides/rate-limits/).

> **Info:** Send many as batch operation items (operation search.jobs); up to 500 jobs per item, 1 credit per job returned. Unlike this sync endpoint, an empty batch item is rejected instead of returning a free empty result. See the [batch endpoint](https://up2data.ai/docs/v1/api/batch-create/).

## Request

| Field | Type | Notes |
|---|---|---|
| `company_url` | `string` | Restrict to one company's jobs. Company page URL or slug. |
| `keywords` | `string[]` | Matched against the posting; several match any of them. |
| `filters.locations` | `string[]` | Place names. An unknown place answers 400. |
| `filters.seniority` | `string[]` | Enum per item: entry, senior, manager, director, executive. senior and manager both map to LinkedIn's mid-senior level. |
| `filters.function` | `string[]` | Job function names, e.g. Engineering, Sales, Information Technology, Marketing. An unknown function answers 400. |
| `filters.workplace_type` | `string[]` | Enum per item: remote, hybrid, onsite. |
| `filters.posted_within_days` | `integer` | Days back from now. 0 or less means no date limit. |
| `max_results` | `integer` | Default 25. 1 to 100; a hard cap on jobs returned by this call and so on cost. Wins over per_page for the page size. |
| `per_page` | `integer` | 1 to 100. Jobs per page, used to work out where a page starts; defaults to max_results. |
| `page` | `integer` | Default 1. Page of per_page jobs to return. |
| `pagination_token` | `string` | Opaque token for a page; used instead of page. |

## Call it

**cURL**

```bash
curl -X POST "https://api.up2data.ai/v1/search/jobs" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "keywords": [
    "golang"
  ],
  "filters": {
    "workplace_type": [
      "remote"
    ],
    "posted_within_days": 7
  },
  "max_results": 25
}'
```

**Python**

```python
import requests

url = "https://api.up2data.ai/v1/search/jobs"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
}
payload = {
    "keywords": [
        "golang"
    ],
    "filters": {
        "workplace_type": [
            "remote"
        ],
        "posted_within_days": 7
    },
    "max_results": 25
}

response = requests.post(url, headers=headers, json=payload)
print(response.json())
```

**Node**

```javascript
const url = "https://api.up2data.ai/v1/search/jobs";
const headers = {
  "X-API-Key": "YOUR_API_KEY",
  "Content-Type": "application/json",
};
const body = {
  "keywords": [
    "golang"
  ],
  "filters": {
    "workplace_type": [
      "remote"
    ],
    "posted_within_days": 7
  },
  "max_results": 25
};

const response = await fetch(url, {
  method: "POST",
  headers,
  body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);
```

**Go**

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	url := "https://api.up2data.ai/v1/search/jobs"
	body := bytes.NewBufferString(`{
  "keywords": [
    "golang"
  ],
  "filters": {
    "workplace_type": [
      "remote"
    ],
    "posted_within_days": 7
  },
  "max_results": 25
}`)
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-API-Key", "YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)
	fmt.Println(string(respBody))
}
```

## Response

**200**

```json
{
  "data": {
    "jobs": [{
      "url": "https://www.linkedin.com/jobs/view/4470130770",
      "title": "Full-Stack Agentic Engineer (focus on Backend)",
      "company": { "name": "Apoddo", "url": "https://www.linkedin.com/company/apoddo-llp", "linkedin_id": "11571939", "logo_url": "https://media.licdn.com/dms/image/v2/D4D0BAQExample/company-logo_200_200/0/apoddo_llp_logo" },
      "location": "Serbia (Remote)",
      "posted_at": "2026-09-25T09:22:27.000Z",
      "workplace_type": "remote",
      "description_snippet": "Apoddo is a software development-as-a-service company...",
      "job_id": "4470130770",
      "employment_type": "full-time"
    }]
  },
  "meta": { "creditsUsed": 25, "creditsRemaining": 98394, "billed": true, "reason": "success", "requestId": "req_example", "latencyMs": 21000 }
}
```

## Errors

| Status | Type | When | Billed |
|---|---|---|---|
| 400 | `invalid_request` | Unknown field, bad enum value, unknown function, or a location or company that matches nothing. | No |
| 401 | `invalid_api_key` | Missing, malformed, or revoked key. | No |
| 402 | `insufficient_credits` | Balance can't cover max_results jobs. | No |
| 429 | `rate_limited` | Over the 300 requests/min limit, or the 20 searches/hour per-organization quota. Honor Retry-After. | No |
| 500 | `upstream_error` | LinkedIn rejected the search query, or something broke on the server side. | No |
| 504 | `upstream_timeout` | The live scrape didn't complete in time. | No |

See the [Errors guide](https://up2data.ai/docs/v1/guides/errors/) for every error type this version can return.
