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

# Search Jobs

> Search job postings by keyword, place, company and filters, 25 per page

## Endpoint

```
GET /api/v1/jobs/search
```

## Authentication

Include your API key in the request header:

```bash theme={null}
X-API-Key: your-api-key-here
```

## Parameters

Every parameter is optional; combine them freely. List parameters take
comma-separated values (`workplaceType=remote,hybrid`). An unknown parameter
name or a value outside a list is rejected with a free `400`, rather than
silently ignored.

**search** `string`

Keywords, matched against the job title and description. At most 200
characters.

**location** `string`

A place, as text (`Lyon`, `Île-de-France`, `San Francisco Bay Area`). It is
resolved to the **first** suggestion of
[Search Geo IDs](https://docs.fetchin.io/api-reference/endpoint/search-geo-ids.md) for that text, and
the response's `query.geoId` / `query.geoTitle` say which place the search
ran on. Pass `geoId` instead to choose the place exactly.

**geoId** `string`

The numeric ID of a place, from
[Search Geo IDs](https://docs.fetchin.io/api-reference/endpoint/search-geo-ids.md) (e.g. `105015875`
for France, `103815258` for Lyon). Overrides `location`.

**companyId** `string`

One or more numeric company IDs (the `companyId` returned by
[Get Company](https://docs.fetchin.io/api-reference/endpoint/get-company.md)), comma-separated.

**sortBy** `string`

`relevance` (the default ordering) or `date` (newest first).

**workplaceType** `string`

`office`, `hybrid`, `remote`.

**employmentType** `string`

`full-time`, `part-time`, `contract`, `temporary`, `internship`.

**experienceLevel** `string`

`internship`, `entry`, `associate`, `mid-senior`, `director`, `executive`.

**postedLimit** `string`

`24h`, `week` or `month`: only jobs posted within that window.

**salary** `string`

Minimum salary: `40k+`, `60k+`, `80k+`, `100k+`, `120k+`, `140k+`, `160k+`,
`180k+`, `200k+`. **Applies to United States searches only**: elsewhere the
source has no salary filter and the results are the same with or without it.

**industryId** `string`

One or more numeric industry IDs, comma-separated (e.g. `4` for Software
Development).

**functionId** `string`

One or more job function codes, comma-separated: `acct`, `adm`, `advr`,
`anls`, `art`, `bd`, `cnsl`, `cust`, `dist`, `dsgn`, `edu`, `eng`, `fin`,
`genb`, `hcpr`, `hr`, `it`, `lgl`, `mgmt`, `mnfc`, `mrkt`, `othr`, `pr`,
`prch`, `prdm`, `prjm`, `prod`, `qa`, `rsch`, `sale`, `sci`, `stra`, `supl`,
`trng`, `wrt`.

**under10Applicants** `boolean` (default: false)

`true` keeps only jobs with fewer than 10 applicants.

**easyApply** `boolean` (default: false)

`true` keeps only jobs you apply to without leaving the platform. `false`
applies no filter: Easy Apply jobs cannot be excluded.

**page** `integer` (default: 1)

Page number, from 1 to 40. Each page holds 25 jobs, and a search reaches at
most its first 1,000 results.

## Response

**elements** `array`

The jobs on this page, up to 25.

**Job object**

**id** `string` *required*

The job posting's ID.

**url** `string` *required*

The job posting's URL.

**title** `string` *required*

Job title.

**postedDate** `string`

When the job was posted (or reposted), ISO 8601.

**company** `object` *required*

The hiring company: `name` always; `universalName`, `companyId`, `url` and
`logoUrl` when the employer has a company page (a job posted by
"Self-employed" or a staffing label has the name only).

**location** `object` *required*

`text`: the location line, with the workplace type in parentheses (e.g.
`"Lyon, Auvergne-Rhône-Alpes, France (Hybrid)"`).

**workplaceType** `string`

`office`, `hybrid` or `remote`, when the posting states it.

**easyApply** `boolean` *required*

Whether the job takes applications without leaving the platform.

**salary** `string`

The pay range the posting displays, as written (e.g.
`"70K EUR/yr - 90K EUR/yr"`). Absent when it shows none.

**promoted** `boolean` *required*

Whether the posting is a paid promotion.

**reposted** `boolean` *required*

Whether the employer reposted the job (`postedDate` is then the repost date).

**pagination** `object`

`totalElements`: the number of jobs the search matches; `totalPages`: the
pages you can request (at most 40, since a search reaches its first 1,000
results); `pageNumber`, `previousElements` (jobs on the pages before this one)
and `pageSize` (25).

**query** `object`

The parameters the search ran with, plus `geoId` and `geoTitle`: the place it
ran on, whether you passed `geoId` or a `location` text.

## Example Request

```bash cURL theme={null}
curl -G "https://api.fetchin.io/api/v1/jobs/search" \
  --data-urlencode "search=sales" \
  --data-urlencode "location=Lyon" \
  --data-urlencode "workplaceType=remote,hybrid" \
  --data-urlencode "postedLimit=week" \
  --data-urlencode "sortBy=date" \
  -H "X-API-Key: your-api-key-here"
```

```javascript JavaScript theme={null}
const params = new URLSearchParams({
  search: 'sales',
  geoId: '103815258',
  workplaceType: 'remote,hybrid',
  postedLimit: 'week',
  sortBy: 'date',
});

const response = await fetch(`https://api.fetchin.io/api/v1/jobs/search?${params}`, {
  headers: { 'X-API-Key': 'your-api-key-here' },
});

const { elements, pagination } = await response.json();
console.log(pagination.totalElements, elements.length);
```

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

response = requests.get(
    'https://api.fetchin.io/api/v1/jobs/search',
    headers={'X-API-Key': 'your-api-key-here'},
    params={
        'search': 'sales',
        'geoId': '103815258',
        'workplaceType': 'remote,hybrid',
        'postedLimit': 'week',
        'sortBy': 'date',
        'page': 1,
    },
)

data = response.json()
print(data['pagination'])
```

## Example Response

```json 200 Success theme={null}
{
  "elements": [
    {
      "id": "4490345452",
      "url": "https://www.linkedin.com/jobs/view/4490345452/",
      "title": "Responsable Grands Comptes - Laboratoires de prothèse dentaire ! H/F",
      "postedDate": "2026-11-12T13:48:18.000Z",
      "company": {
        "name": "Upward",
        "universalName": "groupe-upward",
        "companyId": "11077529",
        "url": "https://www.linkedin.com/company/groupe-upward/",
        "logoUrl": "https://media.licdn.com/dms/image/..."
      },
      "location": {
        "text": "Lyon, Auvergne-Rhône-Alpes, France (Hybrid)"
      },
      "workplaceType": "hybrid",
      "easyApply": true,
      "salary": "70K EUR/yr - 90K EUR/yr",
      "promoted": false,
      "reposted": false
    }
  ],
  "pagination": {
    "totalPages": 4,
    "totalElements": 95,
    "pageNumber": 1,
    "previousElements": 0,
    "pageSize": 25
  },
  "query": {
    "search": "sales",
    "location": "Lyon",
    "geoId": "103815258",
    "geoTitle": "Lyon, Auvergne-Rhône-Alpes, France",
    "sortBy": "date",
    "workplaceType": "remote,hybrid",
    "postedLimit": "week",
    "page": 1
  }
}
```

```json 400 Bad Request theme={null}
{
  "error": "'workplaceType' accepts office, remote, hybrid (received 'onsite').",
  "code": "INVALID_PARAMETER"
}
```

```json 401 Unauthorized theme={null}
{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY"
}
```

```json 402 Payment Required theme={null}
{
  "error": "Quota exceeded. Upgrade your plan or wait for renewal.",
  "code": "QUOTA_EXHAUSTED",
  "details": {
    "creditsRemaining": 0,
    "subscriptionCreditsRemaining": 0,
    "paygCreditsRemaining": 0,
    "buyCreditsUrl": "https://fetchin.io/credits"
  }
}
```

```json 503 Service Unavailable theme={null}
{
  "error": "Service temporarily unable to serve this request. Please retry.",
  "code": "SERVICE_UNAVAILABLE"
}
```

## Errors

See [Error Handling](https://docs.fetchin.io/concepts/error-handling.md) for the full list of error codes and recommended handling.

* `400` — `INVALID_PARAMETER`: an unknown parameter, a value outside a list, a
  `page` outside 1-40, or a `location` that matches no place
* `401` — `INVALID_API_KEY`
* `402` — `QUOTA_EXHAUSTED`
* `429` — `RATE_LIMITED`
* `500` — `INTERNAL_ERROR`
* `503` — `SERVICE_UNAVAILABLE`

A search that matches nothing answers `200` with an empty `elements` list.

## Notes

> [!NOTE]
>
> This endpoint consumes 1 credit per page. Errors are free. See [Quotas](https://docs.fetchin.io/concepts/quotas.md).

> [!TIP]
>
> A city with `workplaceType=remote` returns the remote jobs posted for that
> city or its region. Remote jobs posted for a whole country (`"France
>     (Remote)"`) come with the country's `geoId`.

> [!NOTE]
>
> With `sortBy=relevance` (or no `sortBy`), the ordering is the source's own
> ranking and can differ between two identical calls. Use `sortBy=date` for a
> stable, newest-first walk through the pages.


## OpenAPI

````yaml GET /api/v1/jobs/search
openapi: 3.1.0
info:
  title: Fetchin API
  version: 1.0.0
  description: Public B2B data API for fetching posts and profile information
servers:
  - url: https://api.fetchin.io
    description: Production server
security:
  - ApiKeyAuth: []
paths:
  /api/v1/jobs/search:
    get:
      summary: Search jobs
      description: >-
        Search job postings by keyword, place, company and filters, 25 per page,
        up to 40 pages (a search reaches its first 1,000 results). List
        parameters take comma-separated values. An unknown parameter or a value
        outside a list is a free 400. One credit per page.
      parameters:
        - name: search
          in: query
          required: false
          description: >-
            Keywords, matched against the job title and description (at most 200
            characters).
          schema:
            type: string
            example: sales
        - name: location
          in: query
          required: false
          description: >-
            A place as text, resolved to the first suggestion of GET
            /api/v1/geo-id/search; `query.geoId` and `query.geoTitle` in the
            response say which place the search ran on.
          schema:
            type: string
            example: Lyon
        - name: geoId
          in: query
          required: false
          description: >-
            Numeric ID of a place, from GET /api/v1/geo-id/search. Overrides
            `location`.
          schema:
            type: string
            example: '105015875'
        - name: companyId
          in: query
          required: false
          description: One or more numeric company IDs, comma-separated.
          schema:
            type: string
            example: '1035'
        - name: sortBy
          in: query
          required: false
          description: '`relevance` (default ordering) or `date` (newest first).'
          schema:
            type: string
            enum:
              - relevance
              - date
        - name: workplaceType
          in: query
          required: false
          description: 'Comma-separated: `office`, `hybrid`, `remote`.'
          schema:
            type: string
            example: remote,hybrid
        - name: employmentType
          in: query
          required: false
          description: >-
            Comma-separated: `full-time`, `part-time`, `contract`, `temporary`,
            `internship`.
          schema:
            type: string
            example: full-time
        - name: salary
          in: query
          required: false
          description: Minimum salary, United States searches only.
          schema:
            type: string
            enum:
              - 40k+
              - 60k+
              - 80k+
              - 100k+
              - 120k+
              - 140k+
              - 160k+
              - 180k+
              - 200k+
        - name: postedLimit
          in: query
          required: false
          description: Only jobs posted within this window.
          schema:
            type: string
            enum:
              - 24h
              - week
              - month
        - name: experienceLevel
          in: query
          required: false
          description: >-
            Comma-separated: `internship`, `entry`, `associate`, `mid-senior`,
            `director`, `executive`.
          schema:
            type: string
            example: mid-senior
        - name: industryId
          in: query
          required: false
          description: One or more numeric industry IDs, comma-separated.
          schema:
            type: string
            example: '4'
        - name: functionId
          in: query
          required: false
          description: >-
            One or more job function codes, comma-separated (`eng`, `it`,
            `sale`, `mrkt`…; the full list is on the endpoint page).
          schema:
            type: string
            example: eng,it
        - name: under10Applicants
          in: query
          required: false
          description: '`true` keeps only jobs with fewer than 10 applicants.'
          schema:
            type: boolean
            default: false
        - name: easyApply
          in: query
          required: false
          description: '`true` keeps only Easy Apply jobs; `false` applies no filter.'
          schema:
            type: boolean
            default: false
        - name: page
          in: query
          required: false
          description: Page number, 1 to 40, 25 jobs per page.
          schema:
            type: integer
            minimum: 1
            maximum: 40
            default: 1
      responses:
        '200':
          description: One page of jobs
          content:
            application/json:
              schema:
                type: object
                properties:
                  elements:
                    type: array
                    items:
                      type: object
                      required:
                        - id
                        - url
                        - title
                        - company
                        - location
                        - easyApply
                        - promoted
                        - reposted
                      properties:
                        id:
                          type: string
                          example: '4490345452'
                          description: The job posting's ID.
                        url:
                          type: string
                          example: https://www.linkedin.com/jobs/view/4490345452/
                        title:
                          type: string
                          example: Account Manager - Lyon
                        postedDate:
                          type: string
                          format: date-time
                          description: When the job was posted (or reposted).
                        company:
                          type: object
                          required:
                            - name
                          description: >-
                            The hiring company. `universalName`, `companyId`,
                            `url` and `logoUrl` are present when the employer
                            has a company page.
                          properties:
                            name:
                              type: string
                              example: Upward
                            universalName:
                              type: string
                              example: groupe-upward
                            companyId:
                              type: string
                              example: '11077529'
                            url:
                              type: string
                              example: https://www.linkedin.com/company/groupe-upward/
                            logoUrl:
                              type: string
                        location:
                          type: object
                          required:
                            - text
                          properties:
                            text:
                              type: string
                              example: Lyon, Auvergne-Rhône-Alpes, France (Hybrid)
                              description: >-
                                The location line, with the workplace type in
                                parentheses.
                        workplaceType:
                          type: string
                          enum:
                            - office
                            - hybrid
                            - remote
                          description: When the posting states it.
                        easyApply:
                          type: boolean
                          description: >-
                            Whether the job takes applications without leaving
                            the platform.
                        salary:
                          type: string
                          example: 70K EUR/yr - 90K EUR/yr
                          description: >-
                            The pay range the posting displays, as written.
                            Absent when it shows none.
                        promoted:
                          type: boolean
                          description: Whether the posting is a paid promotion.
                        reposted:
                          type: boolean
                          description: Whether the employer reposted the job.
                  pagination:
                    type: object
                    properties:
                      totalPages:
                        type: integer
                        description: Pages you can request, at most 40.
                      totalElements:
                        type: integer
                        description: Jobs the search matches.
                      pageNumber:
                        type: integer
                      previousElements:
                        type: integer
                        description: Jobs on the pages before this one.
                      pageSize:
                        type: integer
                        example: 25
                  query:
                    type: object
                    description: >-
                      The parameters the search ran with, plus `geoId` and
                      `geoTitle`: the place it ran on.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  responses:
    BadRequest:
      description: Bad request - a required parameter is missing or a value is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missingParameter:
              value:
                error: Missing profileUrlOrUrn parameter
                code: MISSING_PARAMETER
            invalidParameter:
              value:
                error: limit must be a number between 1 and 100
                code: INVALID_PARAMETER
            invalidUrn:
              value:
                error: 'Invalid URN format: urn:li:member:123456789'
                code: INVALID_URN
    Unauthorized:
      description: Unauthorized - the X-API-Key header is missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missingApiKey:
              value:
                error: Missing API key. Include X-API-Key header.
                code: UNAUTHENTICATED
            invalidApiKey:
              value:
                error: Invalid API key
                code: INVALID_API_KEY
    PaymentRequired:
      description: >-
        Payment required - you are out of credits (QUOTA_EXHAUSTED): the plan
        allowance for the current period is spent AND no pay-as-you-go credits
        are left. Buy pay-as-you-go credits (self-serve,
        https://fetchin.io/credits), upgrade your plan, or wait for renewal;
        retrying will not help and no Retry-After is sent. Distinct from the
        per-second rate limit, which is a 429 (RATE_LIMITED).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            quotaExhausted:
              value:
                error: Quota exceeded. Upgrade your plan or wait for renewal.
                code: QUOTA_EXHAUSTED
                details:
                  creditsRemaining: 0
                  subscriptionCreditsRemaining: 0
                  paygCreditsRemaining: 0
                  buyCreditsUrl: https://fetchin.io/credits
    TooManyRequests:
      description: >-
        Too many requests - your per-second rate limit (RATE_LIMITED) was
        exceeded. Back off and retry, honoring the Retry-After header. (Running
        out of credits is a separate 402 PaymentRequired / QUOTA_EXHAUSTED, and
        buying credits never raises this per-second limit.)
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying the request.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            rateLimited:
              value:
                error: Rate limit exceeded. Try again later.
                code: RATE_LIMITED
    InternalError:
      description: >-
        Internal server error - an unexpected error occurred in our API. Safe to
        retry; contact support if it persists.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            internalError:
              value:
                error: Internal server error
                code: INTERNAL_ERROR
    ServiceUnavailable:
      description: >-
        Service unavailable - our API is temporarily unable to serve this
        request (capacity exhausted or overloaded). This is on our side, not
        yours and not an upstream service outage. Retry with backoff, honoring
        the Retry-After header.
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying the request.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            serviceUnavailable:
              value:
                error: >-
                  Service temporarily unable to serve this request. Please
                  retry.
                code: SERVICE_UNAVAILABLE
  schemas:
    Error:
      type: object
      required:
        - error
        - code
      properties:
        error:
          type: string
          description: >-
            Human-readable description of what went wrong. For display/logging;
            do not branch on this string.
        code:
          type: string
          enum:
            - MISSING_PARAMETER
            - INVALID_PARAMETER
            - INVALID_URN
            - UNAUTHENTICATED
            - INVALID_API_KEY
            - FORBIDDEN
            - NOT_FOUND
            - PROFILE_NOT_FOUND
            - POST_NOT_FOUND
            - COMPANY_NOT_FOUND
            - ENDPOINT_REMOVED
            - RATE_LIMITED
            - QUOTA_EXHAUSTED
            - INTERNAL_ERROR
            - UPSTREAM_ERROR
            - SERVICE_UNAVAILABLE
            - UPSTREAM_TIMEOUT
          description: >-
            Stable, machine-readable error code. Branch your integration on this
            value, not on the HTTP status alone or the message text.
        details:
          type: object
          additionalProperties: true
          description: >-
            Optional structured context. On QUOTA_EXHAUSTED it carries the
            balance breakdown at the moment of the rejection - creditsRemaining,
            subscriptionCreditsRemaining, paygCreditsRemaining - plus
            buyCreditsUrl, the self-serve page for buying more credits. Always
            additive and never guaranteed: read it for diagnostics and logs, but
            branch on `code` and do not require any field of it.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

````

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