# TotalJobs UK \[only $2/1k] Scraper ·  Jobs + Salary + Geo (`memo23/totaljobs-scraper`) Actor

\[only $2] Map the UK job market from TotalJobs.com — 44,490+ postings across 1,780+ listing pages. Each row carries title, employer + logo, location + lat/lng, parsed salary band (min/max/currency/period), dates, and employment type. One outputrecord per job. Apify Residential GB required

- **URL**: https://apify.com/memo23/totaljobs-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Automation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## TotalJobs UK Scraper

Scrape job postings from **TotalJobs.com** (UK) — title, employer + logo, location with lat/lng, **parsed salary band** (min / max / currency / period), `datePosted`, `validThrough`, employment type, industry, and `directApply` flag. One flat row per job from rich `JobPosting` JSON-LD.

![How TotalJobs UK Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-totaljobs.png)

### Why this actor

TotalJobs is one of the largest UK job boards (~44,490 postings, ~1,780 listing pages). It runs behind **Akamai Bot Manager** — direct connections and US/non-GB residential proxies return 403 "Access Denied" from Akamai's edge. The Kong API gateway (`gateway.totaljobs.com`) is **VPC-internal** and never exposed publicly, so the iOS app is not a scrape shortcut.

The actor solves all that:

- **Apify Residential GB** with per-request session rotation (only pool that returns clean 200 OK)
- **JobPosting JSON-LD extraction** — every detail page ships 14 fields server-side; no DOM walking needed for the core data
- **Parsed salary band** — TotalJobs doesn't put salary in JSON-LD `baseSalary`, so we regex the visible HTML (`£70,967 to £83,926 per annum` → `{min: 70967, max: 83926, currency: "GBP", period: "annum"}`)
- **Auto-pagination** — follows `rel="next"` or `?page=N` until `maxItems` rows are emitted **for that listing URL** (each listing gets its own budget)
- **Mixed input** — listing URLs auto-paginate + emit one row per detail; direct detail URLs scrape one row each

### Use cases

- **Recruitment market intelligence** — salary benchmarking by region/role, employer activity tracking
- **Sales prospecting** — find companies hiring in your target verticals + locations
- **HR competitive analysis** — compare your salary bands against TotalJobs market signal
- **ATS / job-aggregator integration** — clean structured input for downstream pipelines
- **Geospatial analytics** — every row carries `location.lat` + `location.lng` from JSON-LD `PostalAddress.geo`

### Input

| Field | Type | Required | Notes |
|---|---|---|---|
| `startUrls` | `string[]` | yes | Mix of listing URLs (`https://www.totaljobs.com/jobs/in-london`, `/jobs/{keyword}/in-{location}`) and direct detail URLs (`/job/{title-slug}/{org-slug}-job{id}`). |
| `maxItems` | `integer` | no | Maximum job rows emitted **per listing URL**. 3 listings × `maxItems: 100` → up to 300 total rows. Direct detail URLs always emit 1 row each. Each row = one paid `outputrecord`. Default `1000`. Free-tier users have a hidden global ceiling of `100` rows. |
| `maxConcurrency` | `integer` | no | Parallel HTTP requests for detail-page fetches. Sweet spot 3–5 via Apify Residential GB. Default `4`. |
| `maxRequestRetries` | `integer` | no | Per-URL retry budget on Akamai 403, proxy CONNECT failures, HTTP/2 stream resets, and network errors. Each retry rotates the proxy session with mild exponential backoff. Default `6`. |
| `proxy` | object | no | **Apify Residential GB required.** Default is wired correctly — don't override unless you know what you're doing. |

#### Example input

```json
{
  "startUrls": [
    "https://www.totaljobs.com/jobs/software-engineer/in-london",
    "https://www.totaljobs.com/jobs/in-manchester"
  ],
  "maxItems": 200,
  "maxConcurrency": 4,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "GB" }
}
````

### Output schema

Every row has `rowType: "job"`. **14 fields from JSON-LD + parsed salary band + structured location.**

```jsonc
{
  "rowType":           "job",
  "listingUrl":        "https://www.totaljobs.com/jobs/in-london",
  "jobId":             "107245246",                       // numeric — stable identifier
  "jobUrl":            "https://www.totaljobs.com/job/regional-optimization-lead/bp-energy-job107245246",
  "title":             "Regional Optimization Lead",

  // ── JobPosting JSON-LD ──
  "description":       "<p>Entity: Supply, Trading & Shipping…</p>",  // HTML
  "datePosted":        "2026-05-07T02:51:32.477Z",          // ISO 8601
  "validThrough":      "2026-06-18T02:51:32.477Z",
  "employmentType":    "FULL_TIME",                         // or null
  "industry":          "Management, Management-Area Management",
  "directApply":       true,
  "jobLocationType":   null,                                // "TELECOMMUTE" for remote
  "applicantLocationRequirements": [],                      // populated when remote-friendly

  // ── Employer (from JSON-LD hiringOrganization) ──
  "employer": {
    "name":            "BP Energy",
    "url":             "https://www.totaljobs.com/jobs/bp-energy?cmpId=1428985&cmp=1",
    "logoUrl":         "https://www.totaljobs.com/CompanyLogos/2e66e5e85ad5408380e06078af3eb663.png"
  },

  // ── Location (from JSON-LD jobLocation.address + geo) ──
  "location": {
    "text":            "St James, London, WC2N 5DU, GB",
    "locality":        "St James",
    "region":          "London",
    "postalCode":      "WC2N 5DU",
    "country":         "GB",
    "lat":             51.50445,
    "lng":             -0.13601
  },

  // ── Salary (parsed from body text via regex — not in JSON-LD) ──
  "salary": {
    "rawText":         "£70,967 to £83,926 per annum",
    "min":             70967,
    "max":             83926,
    "currency":        "GBP",                               // ISO code (GBP/USD/EUR)
    "period":          "annum"                              // "annum" / "hour" / "day" / "week" / "month"
  },

  // ── Apply flow ──
  "applyUrl":          null,                                // JS-resolved at click — see Notes below
  "applyType":         "internal",                          // "internal" / "external" / "unknown" (from directApply)

  "scrapedAt":         "2026-05-15T06:25:31.012Z"
}
```

### How it works

1. **Classify input** — listings (`/jobs/...`) vs. details (`/job/{slug}-job{id}`). Listings auto-paginate; details scrape one row each.
2. **Fetch via Apify Residential GB** — `impit` with Firefox TLS fingerprint. Direct + Evomi residential get 403 Akamai blocks; Apify GB returns clean 200 OK.
3. **For listings**: collect detail-URL anchors per page, follow `rel="next"` until empty or cap. Then concurrent detail fetches via sliding window.
4. **For each detail**: parse `JobPosting` JSON-LD for 14 fields. Run salary regex on body text. Emit one flat row.

### ⚠️ Apply URL limitation

TotalJobs renders the apply button server-side as a **disabled placeholder** (`<button aria-label="apply-button-placeholder" disabled>`). The real apply URL is loaded **async via XHR after click**, triggered by JS event listeners we can't execute without a browser.

We tested 15 standard REST-style apply-API endpoints (e.g. `/api/applicationredirect/{id}`, `/api/v1/listings/{id}/apply`) — all returned 404. The apply data lives in `__PRELOADED_STATE__.applyNowSection` but contains only `listingId` + `listingGlobalId`, not the resolved external URL.

**v1 ships `applyUrl: null` and `applyType` derived from JSON-LD's `directApply` flag**:

- `applyType: "internal"` → TotalJobs hosts the application (`directApply: true`)
- `applyType: "external"` → External recruiter hosts (`directApply: false`)
- `applyType: "unknown"` → JSON-LD didn't specify

Buyers needing the actual recruiter URL should open the `jobUrl` in a browser and click apply — or wait for v1.1 which will reverse-engineer the JS XHR call. **No competing TotalJobs scraper on Apify Store currently provides `applyUrl` either** — this is a TotalJobs-platform limitation, not a scraper gap.

### Notes & limitations

- **Apify Residential GB is mandatory.** Direct connections from non-UK IPs get 403 Akamai blocks. Evomi residential (any country) also gets blocked. Apify Residential GB returns clean 200 OK on every probe we tested.
- **Per-listing-page result count varies.** Some listing pages return 10 detail anchors, some 7, occasionally 0 (TotalJobs hides duplicate-job rows). The pagination loop handles empty pages gracefully and continues to page N+1.
- **`employmentType` fill ≈ 70%.** Not every JobPosting JSON-LD declares it. We don't synthesize when missing.
- **`jobLocationType` only set for remote/hybrid roles.** Non-remote jobs leave it `null` — matches JSON-LD semantics.
- **Salary fill ≈ 100% when surfaced.** When a job has a published salary it parses cleanly; when it doesn't (small fraction of jobs) the `salary` field is `null`.
- **`/jobs/{company-slug}-jobs` URLs work** for company-specific listings (vs the `/jobs/in-{location}` keyword listings).

### FAQ

**Which TotalJobs URLs work?**
Two types: **listing URLs** (`/jobs/in-london`, `/jobs/software-engineer/in-manchester`, `/jobs/{company-slug}-jobs`) which auto-paginate and emit one row per linked job, and **direct detail URLs** (`/job/{title-slug}/{org-slug}-job{id}`) which scrape one row each. You can mix both in the same `startUrls` array.

**Why do I need Apify Residential GB?**
TotalJobs sits behind Akamai Bot Manager with strict country rules. Direct connections, datacenter proxies, and non-GB residential (we tested Evomi IN/US/EU) all return 403 "Access Denied" from Akamai's edge. Apify Residential GB is the only pool we found that returns clean 200 OK on every probe.

**Why is `applyUrl` always `null`?**
TotalJobs renders the apply button server-side as a disabled placeholder — the real URL loads async via XHR after click. We can't execute that JS without a browser. The `applyType` field (derived from JSON-LD's `directApply` flag) tells you whether the application is hosted internally (`directApply: true`) or by an external recruiter (`directApply: false`). See the ⚠️ Apply URL section above for the full breakdown.

**What does each `outputrecord` charge cover?**
One job row with all 14 JSON-LD fields plus the parsed salary band (min/max/currency/period) and structured location (lat/lng). `maxItems` is **per listing URL**, so a `maxItems: 100` run with 2 listings = up to 200 charges.

**Can the parsed salary handle annual / hourly / ranges?**
Yes. The regex catches `£70,967 to £83,926 per annum`, `£50k - 70k per year`, `£15 per hour`, and single values. The `period` field normalizes to `annum`/`hour`/`day`/`week`/`month`. When a job has no published salary, `salary` is `null` — we don't synthesize.

**Why does one listing page sometimes return 7 jobs and another 10?**
TotalJobs hides duplicate-job rows on listing pages. The pagination loop handles empty / short pages gracefully and continues to page N+1 until either empty or `maxItems` rows have been emitted for that listing.

### Support

- **Bugs / feature requests** — open an issue on the GitHub repo
- **Custom exports / tailored fields** — drop a note via the Apify Store contact form
- **Other actors** — see my Apify Store profile for the rest of the catalog

***

### ⚠️ Disclaimer

This Actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by** TotalJobs.com, StepStone Group, or any of their subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper extracts only **publicly visible** job postings rendered server-side by TotalJobs — no login, no CAPTCHA solving, no API-key forgery, no `gateway.totaljobs.com` (VPC-internal) probing. The actor honours `robots.txt` and rate-limits via concurrency cap (default 4) to avoid burdening TotalJobs's infrastructure.

Users are responsible for:

- Complying with TotalJobs.com's Terms of Service
- Following UK GDPR + your jurisdiction's data-protection laws when storing or processing scraped postings
- Not contacting candidates listed by employers in scraped postings
- Not republishing scraped data in a way that competes commercially with TotalJobs

***

### SEO Keywords

totaljobs scraper, scrape totaljobs, totaljobs uk scraper, totaljobs.com scraper, totaljobs api, Apify totaljobs, uk jobs scraper, uk job board scraping, jobpostings api, jobposting json-ld scraper, uk recruitment api, recruitment scraper uk, uk salary data, salary band extraction, uk job market data, hiring intelligence uk, employer hiring data, b2b sales prospecting uk, london jobs scraping, manchester jobs scraper, edinburgh jobs scraper, akamai bot manager bypass, apify residential gb

# Actor input Schema

## `startUrls` (type: `array`):

Mix of listing URLs (e.g. `https://www.totaljobs.com/jobs/in-london`, `https://www.totaljobs.com/jobs/software-engineer/in-london`) and direct job-detail URLs (e.g. `https://www.totaljobs.com/job/senior-engineer/acme-job107245245`). Listings auto-paginate via `?page=N` until your `maxItems` budget is hit. Details emit one row each.

## `maxItems` (type: `integer`):

Maximum job rows emitted **per listing URL**. Pass 3 listings with `maxItems: 100` → up to 300 total rows (100 each). Direct detail URLs always emit 1 row each. Each row is one paid `outputrecord` event. Default 1000. Free-tier users are additionally capped at 100 total rows across the whole run.

## `maxConcurrency` (type: `integer`):

Parallel HTTP requests for detail-page fetches. TotalJobs is Akamai-protected — concurrency 3-5 is the sweet spot via Apify Residential GB.

## `maxRequestRetries` (type: `integer`):

Per-URL retry budget on network errors, proxy CONNECT failures, HTTP/2 stream resets, and 403 Akamai blocks. Each retry rotates the proxy session. Default 6.

## `proxy` (type: `object`):

Apify Residential GB required. TotalJobs is Akamai-protected — direct connections + Evomi residential return 403 Akamai 'Access Denied'. Only Apify Residential GB returns clean 200 OK.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.totaljobs.com/jobs/in-london"
  ],
  "maxItems": 1000,
  "maxConcurrency": 4,
  "maxRequestRetries": 6,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}
```

# 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 = {
    "startUrls": [
        "https://www.totaljobs.com/jobs/in-london"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "GB"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/totaljobs-scraper").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 = {
    "startUrls": ["https://www.totaljobs.com/jobs/in-london"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/totaljobs-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.totaljobs.com/jobs/in-london"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}' |
apify call memo23/totaljobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=memo23/totaljobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "TotalJobs UK [only $2/1k] Scraper ·  Jobs + Salary + Geo",
        "description": "[only $2] Map the UK job market from TotalJobs.com — 44,490+ postings across 1,780+ listing pages. Each row carries title, employer + logo, location + lat/lng, parsed salary band (min/max/currency/period), dates, and employment type. One outputrecord per job. Apify Residential GB required",
        "version": "0.0",
        "x-build-id": "9ObHbKua6LbIGdEhr"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/memo23~totaljobs-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-memo23-totaljobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/memo23~totaljobs-scraper/runs": {
            "post": {
                "operationId": "runs-sync-memo23-totaljobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/memo23~totaljobs-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-memo23-totaljobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "startUrls"
                ],
                "properties": {
                    "startUrls": {
                        "title": "TotalJobs URLs",
                        "type": "array",
                        "description": "Mix of listing URLs (e.g. `https://www.totaljobs.com/jobs/in-london`, `https://www.totaljobs.com/jobs/software-engineer/in-london`) and direct job-detail URLs (e.g. `https://www.totaljobs.com/job/senior-engineer/acme-job107245245`). Listings auto-paginate via `?page=N` until your `maxItems` budget is hit. Details emit one row each.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxItems": {
                        "title": "Max jobs per listing URL",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum job rows emitted **per listing URL**. Pass 3 listings with `maxItems: 100` → up to 300 total rows (100 each). Direct detail URLs always emit 1 row each. Each row is one paid `outputrecord` event. Default 1000. Free-tier users are additionally capped at 100 total rows across the whole run.",
                        "default": 1000
                    },
                    "maxConcurrency": {
                        "title": "Max parallel requests",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "Parallel HTTP requests for detail-page fetches. TotalJobs is Akamai-protected — concurrency 3-5 is the sweet spot via Apify Residential GB.",
                        "default": 4
                    },
                    "maxRequestRetries": {
                        "title": "Max request retries",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Per-URL retry budget on network errors, proxy CONNECT failures, HTTP/2 stream resets, and 403 Akamai blocks. Each retry rotates the proxy session. Default 6.",
                        "default": 6
                    },
                    "proxy": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify Residential GB required. TotalJobs is Akamai-protected — direct connections + Evomi residential return 403 Akamai 'Access Denied'. Only Apify Residential GB returns clean 200 OK.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ],
                            "apifyProxyCountry": "GB"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
