# LinkedIn Jobs Scraper — Descriptions, Salaries, Seniority (`architekture/linkedin-jobs-monitor`) Actor

LinkedIn jobs (linkedin.com) public job search: title, company, location, posted date, description, salary with evidence, seniority, employment type, industries, workplace. By keywords and location; one-off export or monitor of new and changed jobs. 20 free previews. Pay per result, no login.

- **URL**: https://apify.com/architekture/linkedin-jobs-monitor.md
- **Developed by:** [architek.earth](https://apify.com/architekture) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 delivered jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## LinkedIn Jobs Scraper — Descriptions, Salaries, Seniority

**Recorded run: 2 jobs from 1 search in 20 seconds** (2026-10-22, build 0.1.13, run `s6ed2iEy2tgqCIHcR`). A bounded canary sample, not a full-catalogue benchmark.

architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.

LinkedIn public job listings with descriptions, salaries, seniority, employment type and company from linkedin.com, by keywords and location, as a one-off export or a saved monitor that returns only new and changed jobs. No LinkedIn login or cookies.

| Source | Market | From | Standout fields |
|---|---|---|---|
| LinkedIn public job search (linkedin.com) | Any location LinkedIn search accepts | $4.00 / 1,000 jobs | `description`, `salaryComponents`, `seniorityLevel` / `employmentType`, `changeType` |

### ✅ Why choose this scraper

#### LinkedIn job descriptions in every paid row

Every paid row carries the full `description` with `detailStatus: "COMPLETE"`, plus `employmentType`, `seniorityLevel` and `industries` from the job page, at the single `job` price. There is no separate detail event.

#### LinkedIn salaries with the text they came from

`salaryRaw` keeps the salary text LinkedIn shows, and `salaryComponents` splits it into `min`, `max`, `currency`, `period`, `kind` (`BASE`, `OTE` or `UNSPECIFIED`) and the `evidence` string it was read from. A run on 2026-10-22 returned `£37,000.00/yr - £60,000.00/yr` as `37000`-`60000` `GBP` per `YEAR`.

#### Only new or changed LinkedIn jobs on a schedule

In `paid` mode a named `monitor` keeps a baseline; with `refreshMonitor: true` a run returns only jobs that are `NEW` or `CHANGED` (with `changedFields` showing previous and current values). A repeat refresh on 2026-10-20 found 10 unchanged jobs, returned 0 rows and charged 0 events (run `j7NlnJuMVRZJOompQ`).

#### Filter LinkedIn jobs by remote, seniority and employment type

`workplace`, `seniority_levels` and `employment_types` are checked against the job page, not only the search card. A job whose value is unknown is left out when a filter needs it, and counted as `unknown` in `RUN_SUMMARY`.

#### Measured on a schedule

A canary task runs a fixed `software engineer` / `New York, United States` search on the live site every day and records rows and seconds; the hero line above is its latest green run.

### ⚠️ Worth knowing up front

- Public search results are not guaranteed exhaustive, chronological or identical between requests. This is not LinkedIn Recruiter or a logged-in search export, and there are no applicant counts, recruiter contacts or job-poster names.
- Salary fields are filled only when the posting publishes pay. A bare `$` does not establish USD, so `currency` can be null; hourly figures are not annualized, and OTE is never treated as base pay.
- `workplace` is set only from explicit phrases such as "this role is fully remote"; otherwise it is `UNKNOWN`, even when the description mentions hybrid working. A `remote`, `hybrid` or `on-site` filter therefore excludes some genuinely matching jobs.
- Missing jobs are never marked closed. A repost under a new job ID is a separate job.
- Each LinkedIn page is tried 3 times. The actor does not yet recognise the end of LinkedIn's results, so a search with no matches, or a scan that runs past the last results page, ends as `BLOCKED` (nothing delivered) or `PARTIAL` (rows kept and charged) rather than complete. A run that reaches `max_rows` or `max_pages` ends `LIMITED` in `RUN_SUMMARY`; that is a deliberate cap, not full coverage.
- Invalid input (`INVALID_INPUT`), a monitor conflict such as a resume with no unfinished scan (`STATE_CONFLICT`), used-up free previews (`PREVIEW_LIMIT`), a LinkedIn page still failing after retries (`BLOCKED` or `PARTIAL`) and the run deadline 60 seconds before the timeout (`TIME_LIMIT`) each end the run with one free status row (`"recordType": "status"`). Successful runs write no status row; their outcome is in the `RUN_SUMMARY` record. If a row's write or charge cannot be confirmed (`DELIVERY_UNCERTAIN`, `CHARGE_RECONCILIATION_REQUIRED`), the run fails and the next run on that monitor stops instead of delivering it again.

### ⚡ Try the example

Each account gets 20 free preview runs: one search page, at most 10 basic job cards (title, company, location, URL, posted date), no descriptions or salaries, and no charge. Preview runs never switch to paid mode.

Open the actor's input page, switch to JSON, paste this and start the run:

```json
{
  "mode": "preview",
  "keywords": "software engineer",
  "location": "New York, United States",
  "max_rows": 10
}
```

For descriptions and salaries, set `"mode": "export"` (a new collection every run) or `"mode": "paid"` with a `monitor` name (saved progress and change tracking); 2 jobs cost $0.008.

### ⚙️ Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `preview` (prefill `export`) | `preview`: free, one page of up to 10 basic cards. `export`: an independent detailed export each run. `paid`: a saved monitor with resume and change tracking. |
| `keywords` | string | — (required) | Job-search text sent to LinkedIn, up to 200 characters. |
| `location` | string | — (required) | Location sent to LinkedIn, interpreted by LinkedIn; not an exact geographic boundary. |
| `monitor` | string | `default` | Saved monitor name in `paid` mode, private to your account; 1-64 letters, digits, underscores or hyphens. Query and filters must stay the same for one monitor; use a new name for a new search. |
| `refreshMonitor` | boolean | `false` | `paid` mode only. Start again from page one against the saved baseline and return only new and changed jobs. Cannot be combined with `continuePreviousRun`; abandons any unfinished scan. |
| `continuePreviousRun` | boolean | `false` | `paid` mode only. Resume this monitor's unfinished scan; job IDs already processed in that scan are skipped. |
| `max_rows` | integer | `100` (prefill `1`) | Maximum delivered rows this run, 1-10,000. Previews stop at 10. |
| `max_pages` | integer | `10` (prefill `1`) | Search pages per scan, 1-100. Raise it when resuming a scan that stopped at `PAGE_LIMIT`. |
| `workplace` | select | `any` | `any`, `remote`, `hybrid` or `on-site`, checked against the job description. Not available in preview. |
| `employment_types` | array | `[]` | Allowed employment types, for example `Full-time`, matched exactly ignoring case. Not available in preview. |
| `seniority_levels` | array | `[]` | Allowed seniority levels, for example `Mid-Senior level`; `Not Applicable` counts as unknown. Not available in preview. |
| `title_terms` | array | `[]` | Every term must occur in the job title, ignoring case. |
| `exclude_companies` | array | `[]` | Company names to leave out, matched exactly ignoring case. |
| `feedback` | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |

Each array takes up to 30 values of up to 100 characters. Only `max_rows`, `max_pages`, `refreshMonitor` and `feedback` may change between runs of one monitor.

### 🧱 Stable fields

These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.

| Field | Type | Meaning |
|---|---|---|
| `id` / `url` | string | LinkedIn job ID and job page URL. |
| `title` / `company` / `location` | string | Job title, company name and location from the search card. |
| `postedDate` | string, nullable | Posted date LinkedIn publishes (`YYYY-MM-DD`). |
| `description` | string, nullable | Full job description; null on preview rows. |
| `detailStatus` | string | `COMPLETE` on paid and export rows, `NOT_FETCHED` on preview rows. |
| `employmentType` / `seniorityLevel` / `industries` | string, nullable | Job criteria from the job page. |
| `workplace` / `workplaceEvidence` | string / array | `remote`, `hybrid`, `on-site` or `UNKNOWN`, and the phrases it was read from. |
| `salaryRaw` | string, nullable | Salary text as LinkedIn shows it. |
| `salaryComponents` | array | `{min, max, currency, period, kind, source, evidence}` per salary range found; `currency` and `period` can be null. |
| `salaryStatus` | string | `PARSED_WITH_EVIDENCE`, `RAW_ONLY`, `DESCRIPTION_ONLY`, `NOT_PUBLISHED` or `NOT_FETCHED`. |
| `changeType` / `changedFields` | string / object | `NEW`, `CHANGED` or `PREVIEW`; `changedFields` maps each changed field to its previous and current value. |
| `firstSeenAt` / `observedAt` | string, nullable / string | When the job was first and last seen by this monitor (ISO time); `firstSeenAt` is null on preview rows. |
| `filterStatus` / `filterReasons` | string / array | `MATCH` on every delivered row. |
| `source` | string | `linkedin`. |
| `eventId` | string | Unique per delivered row; use it to de-duplicate across runs. |
| `recordType` | string | `"status"` on free status rows; absent on job rows. |

Status rows carry only `recordType`, `status` and `message`. Other fields are extras, not part of the contract. To have one pinned, say so in `feedback`.

### 📦 Output example

Abridged real row from run `J9Zcgo4rkNUpT8Qdy` on 2026-10-22 (`export` mode, `data analyst` in `London, United Kingdom`); values are a historical snapshot.

```json
{
  "id": "4476590411",
  "title": "Data Analyst",
  "company": "Searchability NS&D",
  "location": "London Area, United Kingdom",
  "url": "https://www.linkedin.com/jobs/view/4476590411",
  "postedDate": "2026-10-07",
  "description": "New Permanent Opportunity for a Security Cleared Data Analyst Must have active SC Clearance Mid to Senior levels available Up to £60k DoE plus package Hybrid working ...",
  "employmentType": "Full-time",
  "seniorityLevel": "Mid-Senior level",
  "industries": "Data Infrastructure and Analytics and IT Services and IT Consulting",
  "workplace": "UNKNOWN",
  "workplaceEvidence": [],
  "source": "linkedin",
  "detailStatus": "COMPLETE",
  "salaryRaw": "£37,000.00/yr - £60,000.00/yr",
  "salaryComponents": [
    {
      "min": 37000,
      "max": 60000,
      "currency": "GBP",
      "period": "YEAR",
      "kind": "UNSPECIFIED",
      "source": "advertised",
      "evidence": "£37,000.00/yr - £60,000.00/yr"
    }
  ],
  "salaryStatus": "PARSED_WITH_EVIDENCE",
  "firstSeenAt": "2026-10-22T10:51:21.663748+00:00",
  "observedAt": "2026-10-22T10:51:21.663748+00:00",
  "changedFields": {},
  "changeType": "NEW",
  "filterStatus": "MATCH",
  "filterReasons": [],
  "eventId": "afc6fb2608009248cb713571ad785814a853d6ef7a83e9b8e8d059ea0abc7514"
}
```

A free status row looks like this (run `FQl1GST3OfsAfpGqf`, 2026-10-22):

```json
{"recordType": "status", "status": "INVALID_INPUT", "message": "Independent exports cannot be resumed; use paid monitor mode"}
```

### 🧠 Advanced usage

#### Monitor new LinkedIn jobs daily for one search

Run `"mode": "paid"` with a fixed `monitor` name, `"refreshMonitor": true` and a small `max_pages` window (for example 1). Each run compares that window with the saved baseline and returns only `NEW` and `CHANGED` jobs; unchanged jobs return no row and are not charged, so a zero-row run can simply mean nothing changed.

#### Continue a large LinkedIn job export across runs

Start a `paid` monitor with your `max_rows`, then rerun the same input with `"continuePreviousRun": true` (and a higher `max_pages` if it stopped at `PAGE_LIMIT`). The scan resumes at its saved page and card; job IDs already processed are skipped and not charged. Each run writes its own dataset, so combine them and de-duplicate on `eventId`.

#### Export a one-off LinkedIn job snapshot

`"mode": "export"` starts a new collection every run, with no saved state and no preview use, so the same input can be repeated. It charges every delivered job, including jobs returned by an earlier export.

#### Check a run before importing it

Read `RUN_SUMMARY` first: `status` is `LIMITED` (row or page cap reached), `PARTIAL` or `FAILED`, with `reason`, `delivered`, `unchanged`, `rejected`, `unknown` and `actualChargeCounts`. For `DELIVERY_UNCERTAIN` or `CHARGE_RECONCILIATION_REQUIRED`, keep the run ID and contact support before rerunning that monitor.

### 🔌 Integrations

Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. A daily `paid` monitor with `refreshMonitor: true` returns only the jobs that are new or changed since the day before. Copy-paste code in Python, Node, curl and n8n: [architek-earth/examples/linkedin-jobs-monitor](https://github.com/architek-earth/examples/tree/main/linkedin-jobs-monitor).

### 💸 Pricing

Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. Each job is charged once, right after its row is written; on a monitor resumed with `continuePreviousRun`, job IDs already processed in that scan are skipped and not charged again, unchanged jobs on a refresh are not charged, and a row whose write was not confirmed stops the next run instead of being delivered again. `export` mode charges every job it delivers, every run.

| Event | Price | What it covers |
|---|---|---|
| Free preview | $0 | 20 runs per account; one page, up to 10 basic cards, no descriptions or salaries. |
| `job` | $4.00 / 1,000 jobs | One delivered job with description and salary fields: `NEW` or `CHANGED` in a monitor, or every row of an export. |

2 jobs cost $0.008; 1,000 jobs cost $4.00. Filtered-out jobs, unchanged jobs and duplicates within a scan are free.

### ❓ FAQ

**Is there a LinkedIn Jobs API for job listings?** This actor is one: it returns LinkedIn's public job search results and job pages as JSON through the Apify API, callable from any language or from n8n, Make and Zapier.

**Do I need a LinkedIn account or cookies?** No. It reads public job pages without logging in; you only need an Apify account.

**Does it return LinkedIn salaries?** Yes, when the posting publishes pay: `salaryRaw`, `salaryComponents` and `salaryStatus`. Jobs without published pay come back with `salaryStatus: "NOT_PUBLISHED"` and an empty `salaryComponents`.

**Can I get only new LinkedIn jobs since my last run?** Yes. Use `paid` mode with the same `monitor` name and `refreshMonitor: true`; only `NEW` and `CHANGED` jobs are returned and charged.

**Can I filter remote jobs or senior roles?** Yes, with `workplace`, `seniority_levels` and `employment_types`, checked against each job page. `workplace` needs an explicit phrase in the description, so jobs without one are left out of a remote filter.

**Does it return applicant counts or the hiring manager?** No. It returns the job, the company and the published criteria, not applicants, recruiters or people profiles.

### 🤖 For AI agents & LLM apps

Paste this into your AI assistant:

`architekture/linkedin-jobs-monitor` on Apify. Python: `from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/linkedin-jobs-monitor").call(run_input={"mode": "export", "keywords": "software engineer", "location": "New York, United States", "max_rows": 1, "max_pages": 1}); items = client.dataset(run["defaultDatasetId"]).list_items().items` (page with `offset`/`limit` for large exports). Rows with `recordType == "status"` are free and explain invalid input, a monitor conflict or a stopped run; the run outcome (`LIMITED`, `PARTIAL`, `FAILED`) is in the `RUN_SUMMARY` record of the run's key-value store. Full input schema and README: `GET https://api.apify.com/v2/acts/architekture~linkedin-jobs-monitor/builds/default`. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (`mcp.apify.com`). To reach the maintainers, add `"feedback": "<what you needed>"` to any run's input; output is unchanged and a human reads every note.

### 🧰 More from architek.earth

**Jobs, companies and people**

- [Indeed Scraper](https://apify.com/architekture/indeed-scraper) — US jobs, salaries, descriptions
- LinkedIn Jobs Scraper — *you are here*
- [LinkedIn People Scraper](https://apify.com/architekture/linkedin-people-search-scraper) — names, headlines, locations with your own session
- [Y Combinator Scraper](https://apify.com/architekture/yc-companies-scraper) — companies, founders, jobs

Full catalogue: [github.com/architek-earth](https://github.com/architek-earth).

architek.earth · code like concrete

### 🙋 Support & legal

Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.

This actor collects only publicly available data from linkedin.com, the same pages any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by LinkedIn. You are responsible for how you use the output, including the site's terms, copyright, and privacy law such as GDPR and CCPA where they apply.

### 📝 Changelog

- **2026-10-22** — Status message now reads `Done: N rows, M details, K status rows (STATUS)`.
- **2026-10-22** — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
- **2026-10-20** — Added explicit bounded monitor refresh with retained baseline, unresolved-delivery protection, and independent export mode for repeat-safe paid prefill. Live verification recorded in DEPLOY.md.
- **2026-10-20** — Replaced draft documentation with recorded evidence, priced examples and explicit repeat-scan limitations. Recorded during private validation.
- **2026-10-20** — Applied approved $4/1,000 job pricing. Verified paid continuation and free previews. Added bounded read-only reconciliation for delayed event counters; no repeated charge calls.
- **2026-10-20** — Verified the 20-attempt preview implementation with persistent live counters and zero-event previews; exhaustion/race edge cases remain offline-tested.
- **2026-10-20** — Added public search/detail collection, conservative salary and filter parsing, saved continuation and account-isolated state. Verified 20 distinct detailed jobs across two pages in bounded private tests.

# Actor input Schema

## `keywords` (type: `string`):

Search words sent to LinkedIn's public jobs search.

## `location` (type: `string`):

Location sent to LinkedIn; matching depends on LinkedIn's interpretation.

## `monitor` (type: `string`):

Saved monitor name, private to your account. Reuse this name to track changes.

## `workplace` (type: `string`):

Verify workplace evidence in job details; unknown or mismatched jobs are excluded.

## `max_rows` (type: `integer`):

Maximum delivered rows for this run. Preview and private validation are capped at 10.

## `max_pages` (type: `integer`):

Maximum search pages in this collection; reaching this limit is not complete coverage.

## `refreshMonitor` (type: `boolean`):

Start a fresh bounded comparison from page one, retaining this monitor baseline. Paid mode only; cannot combine with continuation. Unresolved deliveries block refresh.

## `feedback` (type: `string`):

Optional feedback logged with the run; does not affect results.

## `employment_types` (type: `array`):

Allowed employment types verified against job details. Empty means no restriction.

## `seniority_levels` (type: `array`):

Allowed seniority levels verified against job details. Empty means no restriction.

## `title_terms` (type: `array`):

Every supplied term must occur in the job title (case insensitive).

## `exclude_companies` (type: `array`):

Exact company names to exclude, ignoring case.

## `mode` (type: `string`):

Preview: 20 free lifetime attempts. Paid: saved monitor. Export: independent detailed export each run at $0.004 per delivered job. Prefill exports one job, up to $0.004; does not consume previews.

## `continuePreviousRun` (type: `boolean`):

Resume this account's saved monitor snapshot. Paid mode only; inputs must match.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "New York, United States",
  "monitor": "default",
  "workplace": "any",
  "max_rows": 1,
  "max_pages": 1,
  "refreshMonitor": false,
  "feedback": "",
  "employment_types": [],
  "seniority_levels": [],
  "title_terms": [],
  "exclude_companies": [],
  "mode": "export",
  "continuePreviousRun": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": "software engineer",
    "location": "New York, United States",
    "max_rows": 1,
    "max_pages": 1,
    "mode": "export"
};

// Run the Actor and wait for it to finish
const run = await client.actor("architekture/linkedin-jobs-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": "software engineer",
    "location": "New York, United States",
    "max_rows": 1,
    "max_pages": 1,
    "mode": "export",
}

# Run the Actor and wait for it to finish
run = client.actor("architekture/linkedin-jobs-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": "software engineer",
  "location": "New York, United States",
  "max_rows": 1,
  "max_pages": 1,
  "mode": "export"
}' |
apify call architekture/linkedin-jobs-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,architekture/linkedin-jobs-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/gJsw21l0HoBvXQlsf/builds/4kvnfhrQLPvXmwfqu/openapi.json
