# AU Grocery Price Comparison - Cheapest retailer + Price History (`gatherworks/au-grocery-price-comparison-history`) Actor

Prices a shopping list across every Australian retailer holding each item, names the cheapest and dearest shop with the saving worked out, judges today's price against that product's own history, and totals the whole basket per retailer against buying each item wherever it is cheapest.

- **URL**: https://apify.com/gatherworks/au-grocery-price-comparison-history.md
- **Developed by:** [GatherWorks](https://apify.com/gatherworks) (community)
- **Categories:**
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.00 / 1,000 product prices

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## 🛒 AU Grocery Price Comparison — Cheapest Retailer + Price History

**Name the products. Get back where each one is cheapest, how much you save by not buying it at the
dearest shop, and whether today's price is actually a good one.**

**Swept daily, kept since March 2024.** Coles, Woolworths and ALDI are re-priced every day and
every price is retained — 30 million observations and counting — so you get today's shelf price
*and* the two and a half years behind it. Every figure carries the date it was seen, and every
product can be judged against what it has actually cost.

***

### 🔍 What is it?

Give it a shopping list. For each item it finds the product, prices it at **every retailer holding
it**, then does the part you would otherwise do by hand:

- 🥇 names the cheapest shop and the dearest, and works out the gap
- 🕰️ places today's cheapest price in that product's **own price range** over 7 days to 2 years
- 🧺 totals the **whole list** per retailer, and tells you what one trip costs against cherry-picking

### ⚡ What can it do?

- **🏪 Compare across retailers in one call.** Coles, Woolworths, ALDI and marketplace sellers sit in
  the same database, so a single item comes back priced at all of them together.
- **💸 Quantify the saving.** `spreadCents` and `savingPct` say what the difference is actually worth,
  per item.
- **📉 Say whether it is a good price.** `verdict` is `good-price`, `typical` or `poor-price` — judged
  against the low, median and high that product has really held, not against a guess.
- **🧺 Price the whole basket.** Which single shop is cheapest for everything, and how much more that
  is than buying each item wherever it is cheapest.
- **🔄 Swept daily.** The major chains are re-priced every day, so a row is typically hours old, not
  weeks — and `observed` and `staleDays` say exactly how old rather than leaving you to assume.
- **📅 Date every price.** A price with no timestamp is a claim, not a fact.
- **⚖️ Refuse a comparison it cannot make honestly.** See below — this is the part most price tools
  get wrong.

#### 🎯 It refuses rather than guesses

Three refusals are built in, and each protects you from a number that looks authoritative and is not:

| Refusal | Why |
|---|---|
| 🚫 A price you cannot act on never wins | A tracker's own "lowest across retailers" figure is a real observation and **not a shop**. Naming it "cheapest" would send you to a checkout that does not exist. |
| ⏱️ Prices seen days apart are flagged, not merged | `comparable: false` and `observationGapDays` ride on the row. Two prices seen a week apart are two facts, not a comparison. |
| 🤷 A thin history gets no verdict | If the database holds no price for most of the window, `verdict` is `unknown` and `verdictBasis` says exactly how much was missing. A confident call off one data point is not one we have earned. |

### ⌨️ Inputs

| Input | Type | Default | What it does |
|---|---|---|---|
| 🛒 `queries` | string\[] | — | **Required.** One product name per line, matched against product names in the database |
| 🔢 `matchesPerQuery` | integer | `3` | Distinct products to price per name. `"milk"` matches many; `"Weet-Bix 375g"` matches one. Each is charged |
| 🌏 `market` | string | `"AU"` | ISO 3166-1 alpha-2. AU is the only market with coverage; another code returns no matches rather than guessing |
| 🚧 `maxItems` | integer | *(none)* | Hard cap on products priced across the run |
| 📉 `priceHistory` | `off`/`30d`/`90d` | `30d` | Verdict + history bar. **Add-on** |
| ⚖️ `unitPrice` | boolean | `true` | Price per 100g/100ml. **Add-on** |
| 🔗 `listingHistory` | boolean | `false` | Every listing ever seen. **Add-on** |
| 🏷️ `stockCodes` | boolean | `false` | Retailer SKUs. **Add-on** |
| 🥣 `nutrition` | boolean | `false` | Nutrition panel. **Add-on** |

### 📊 What data does it return?

One row per product, with every retailer's offer nested inside it.

| Field | Notes |
|---|---|
| 🏷️ `name`, `brand`, `gtin`, `productRef` | `productRef` is stable — keep it to re-price the same product later without searching again |
| 📦 `size`, `sizeBase`, `sizeKnown` | The pack size **as the shop printed it** (`375g`, `2 x 1L`), plus that quantity normalised. `sizeKnown` is false when no shop published one — see below |
| 🥇 `bestRetailer`, `bestPriceCents`, `bestListing` | Where it is cheapest, and the page it was seen on |
| 💰 `dearestRetailer`, `dearestPriceCents` | The other end of the range |
| 💸 `spreadCents`, `savingPct` | What shopping around is worth on this item |
| 🏪 `retailerCount` | How many shops the figure is drawn from |
| 📅 `bestObserved`, `bestStaleDays` | When that price was seen, and how long ago |
| ⚖️ `comparable`, `observationGapDays` | Whether the compared prices were seen close enough together to mean anything |
| 🚩 `bannerPresent` | True when a banner (e.g. IGA) is involved, whose stores price independently |
| 📉 `verdict`, `verdictBasis` | `good-price` / `typical` / `poor-price` / `unknown`, and the reasoning |
| 📈 `windowLowCents`, `windowMedianCents`, `windowHighCents` | The range this product has actually held |
| 🔀 `vsMedianPct`, `vsLowCents`, `priceDirection`, `priceChanges` | How today sits against that range, and which way it is moving |
| 🔬 `windowCoveragePct` | How much of the window the database can actually account for |
| 🧮 `sourcesConfirming` | How many independent sources stand behind the price — a count, so you can weigh the evidence |
| ⚠️ `disputedListings`, `widestDisputeCents` | Listings where sources disagreed on the price. Where they do, at least one is wrong, and you should know |
| 📊 `historyBar[]` | *(history add-on)* The price series as intervals, ready to chart. `carried` marks an interval the price was assumed rather than seen |
| 🎯 `windowDearerPct` | *(history add-on)* Share of the window, by time, the price sat **above** where it stands now. 89 means it was dearer than this 89% of the time |
| ⏳ `daysAtCurrentPrice`, `longestHeldCents`, `longestHeldDays` | *(history add-on)* How long today's price has held, and the price that held longest |
| 🔀 `lastChangeCents`, `lastChangePct` | *(history add-on)* The most recent move, and how big it was |
| ⚖️ `per100Cents`, `measure`, `cheapestPackIsCheapestUnit`, `estimated` | *(unit price add-on)* |
| 🔗 `listingHistory[]` | *(listing add-on)* Every listing: link, `firstSeen`, `lastSeen`, `observations`, `stillListed` |
| 🏷️ `stockCodes[]` | *(stock code add-on)* Each retailer's SKU |
| 🥣 `nutrition` | *(nutrition add-on)* `available: false` with a reason where none is held |
| 🧾 `offers[]` | Every retailer: price, listing, observed, staleDays, corroboration |
| ⚠️ `disclaimer` | Carried onto every row, not left in an envelope |

Plus **one basket row per run**, also saved to the key-value store as `BASKET-SUMMARY`:

| Field | Notes |
|---|---|
| 🧺 `byRetailer[]` | Every retailer: how many of your items they carry, the total for those, and **`missing` — the items they do not stock**, because a cheap-looking basket is usually a short one |
| 🥇 `cheapestFullBasket` | The cheapest single shop that carries **everything** — `null` if none does |
| 🗺️ `bestCoverage` | Who came closest, when no single shop carries the lot |
| ✂️ `cherryPickTotalCents` | The total if you buy each item wherever it is cheapest |
| 🚗 `singleStorePremiumCents` | What one trip costs you over cherry-picking. This is the number that decides whether a second stop is worth it |

#### 🚫 What it does **not** return

- **Live prices.** Every row is an recorded observation. The retailer may have changed it since — hence
  the disclaimer on every row.
- **Stock levels you can trust.** `inStock` is often `null`. It is passed through, never inferred.
- **A verdict it cannot support.** Sparse history returns `unknown`, on purpose.
- **Which price tracker the observation came from.** Sources are not disclosed; the retailer you
  would actually buy from is.

### 📄 Real output

From `{"queries": ["weet-bix breakfast cereal"], "priceHistory": "90d", "unitPrice": true}` — a real
run against live data, not an illustration:

```json
{
  "name": "Weet-bix Breakfast Cereal",
  "size": "575g",
  "sizeBase": 575,
  "sizeKnown": true,
  "gtin": "9300652805048",
  "productRef": "prod_HpNtNhDVHVvxx2YGfXF_SA",

  "retailerCount": 2,
  "bestRetailer": "coles",
  "bestPriceCents": 350,
  "bestListing": "https://coles.com.au/product/2001784",
  "sourcesConfirming": 3,
  "dearestRetailer": "woolworths",
  "dearestPriceCents": 500,
  "spreadCents": 150,
  "savingPct": 30,

  "comparable": true,
  "observationGapDays": 0,
  "disputedListings": 2,
  "widestDisputeCents": 90,

  "verdict": "typical",
  "verdictBasis": "Judged against a 250c-500c range over 90d, 98.6% covered.",
  "windowLowCents": 250,
  "windowMedianCents": 500,
  "windowHighCents": 500,
  "windowDearerPct": 86,
  "daysAtCurrentPrice": 0,
  "lastChangeCents": -150,
  "priceDirection": "volatile",
  "priceChanges": 15,

  "cheapestByUnitRetailer": "coles",
  "per100Cents": 61,
  "measure": "mass",
  "cheapestPackIsCheapestUnit": true,
  "estimated": false,

  "disclaimer": "Not a live observation, confirm with retailer"
}
```

Read that row out loud and it says something no single shop can tell you:

- 🥇 **The same 575g box is $3.50 at Coles and $5.00 at Woolworths on the same day** — 30% apart, and
  `comparable: true` with `observationGapDays: 0` means both prices were seen in the same sweep, so
  the gap is real rather than an artefact of looking on different days.
- 📦 **`size: "575g"` is what makes that a comparison at all.** Same pack, both shops.
- ⚖️ **61c per 100g**, and the cheapest box is also the cheapest per gram — which is not always true.
- 📉 **$3.50 is `typical`, not a bargain.** It beats the $5.00 median, but this product has been as
  low as $2.50 in 90 days across 15 price changes, so the verdict declines to call it a good price.
- 🧮 **Three sources confirm the winning price**, and ⚠️ **two listings have sources that disagree**,
  by as much as 90c. At least one of those is wrong, and you get told rather than handed one
  confident number.

### 🧩 Add-ons

Everything below is **opt-in and separately priced**. Leave them off and you pay for prices only.
An add-on is charged **only when the lookup actually answered** — a product with no nutrition on
file is never billed for nutrition.

| Add-on | What it adds | Price |
|---|---|---|
| 📉 **Price history — 1 month** | Verdict (good / typical / poor) against the range the product actually held, plus a chartable history bar | 💵 $0.006 per product |
| 📊 **Price history — 3 months** | The same, through a full promotion cycle — a price that looks good against a month often reads as typical against a quarter | 💵 $0.012 per product |
| ⚖️ **Unit price** | Price per 100g / 100ml at each retailer, and whether the cheapest pack is also the cheapest per unit | 💵 $0.004 per product |
| 🔗 **Listing history** | Every listing ever seen: the link, first and last sighting, how many observations stand behind it, whether it is still listed | 💵 $0.005 per product |
| 🏷️ **Retailer stock codes** | Each retailer's own SKU, for matching to your catalogue or a supplier feed | 💵 $0.004 per product |
| 🥣 **Nutrition** | The nutrition panel where one is held. Batched, so one charge covers up to ten products | 💵 $0.01 per batch |

#### 📦 Why pack size is on every row

**A price gap is not a comparison until you know the two packs are the same
size.** $3.50 against $5.00 means nothing if one box is 375g and the other is
575g — and until recently that field was not published at all, which made every
cross-retailer figure weaker than it looked.

`size` is what the shop printed. It is never computed: a size *is* derivable by
dividing a shelf price by a per-100g price, and that returns 266.67g for a box
printed 266g — a number no shop wrote, in a field you would reasonably read as
the shop's own words. Where nobody published one, `sizeKnown` is `false` and
`size` is absent rather than guessed.

#### 🎁 Included at no extra charge

These ride along with calls you have already paid for, so they cost nothing on top:

- 🧮 **How many sources confirm each price.** Three independent sightings is a different quality of
  evidence from one, and now you can see which you have.
- ⚠️ **Where sources disagree.** `disputedListings` and `widestDisputeCents` flag listings whose
  sources report different prices. At least one of them is wrong — worth knowing before you act on
  a thin margin. In live testing this fired on a real product with a **90c** spread between claims.
- 🎯 **`windowDearerPct`** with any history add-on: the share of the window the price sat *above*
  today's. One number, no interpretation needed.
- ⏳ **How long the current price has held**, the price that held longest, and the size of the last
  move — all free with history.
- 🧺 **What each retailer does *not* stock**, in the basket row. A retailer looking cheap because
  they carry four of your ten items is the trap this closes.
- 📦 **Pack size**, so a price comparison is a comparison. Free on every row, add-on or not.

#### ⚖️ Why the unit price add-on earns its keep

The cheapest box is regularly **not** the cheapest buy. `cheapestPackIsCheapestUnit` answers that
outright instead of leaving you to divide by pack size in your head — and where a size was estimated
rather than published, `estimated: true` says so, because a unit price built on a guessed divisor is
not the same claim as one built on a printed weight.

### 💰 How much does it cost?

Pay-per-event. You are charged for products actually priced and saved — never for a name that
matched nothing, and never for a failed lookup.

| Base event | Price |
|---|---|
| ▶️ Run start | 💵 $0.00005 |
| 🏪 Product priced across retailers | 💵 $0.005 |
| 🧺 Basket comparison (once per run) | 💵 $0.02 |

**You are never charged for work you cannot read.** Every row is billed at the moment it is saved,
so a charge cannot exist without a row in your dataset. A name that matched nothing, a lookup that
failed, an add-on with no answer to give — none of them bill. And if you stop the run, or it reaches
the spending limit you set, whatever was already priced is saved and billed before it exits, rather
than being thrown away after the work was already done.

Worked examples:

| What you run | Cost |
|---|---|
| 🏃 20 products, prices only — every add-on off | 💵 **$0.12** |
| 🥖 20 products + 1-month history | 💵 **$0.24** |
| 🛒 20 products + 1-month history + unit price | 💵 **$0.32** |
| 📦 20 products + 3-month history + unit price + listings + codes | 💵 **$0.62** |
| 🍱 20 products, everything on | 💵 **$0.64** |

A weekly 20-item shop with history and unit prices runs about **$1.28 a month**. Prices only is
**$0.48 a month**.

#### 📈 Against the alternatives

Price history is the part nobody else sells. Dedicated price-intelligence platforms put it behind
their top tier — [Prisync](https://www.g2.com/products/prisync/pricing) unlocks price history on its
**$399/month** plan — and the per-result scrapers do not have it at all: the closest one tells you to
build history yourself by scheduling repeated runs, which means you start accumulating from today.

| | This Actor | Per-result scrapers | Price-intelligence SaaS |
|---|---|---|---|
| 📉 Price history | ✅ back to March 2024 | ❌ starts when you do | ✅ top tier only |
| 🏪 Across retailers in one call | ✅ | ❌ one retailer per Actor | ✅ |
| ⚖️ Unit price comparison | ✅ | ❌ | ➖ sometimes |
| 🧺 Basket vs cherry-picking | ✅ | ❌ | ❌ |
| 💵 Minimum spend | **none** | none | 💸 $99–$399 / month |

Below roughly 800 products a month, this is cheaper than the entry SaaS tier — and you are not
buying capacity you will not use.

### ❓ FAQ

**How fresh is the data?**
The major chains are swept every day. In practice a Coles, Woolworths or ALDI price is a few hours
to a day old, and `observed` and `staleDays` tell you precisely which — a price is never presented
as current when it is not. Smaller retailers and marketplace sellers are swept less often, and their
`staleDays` says so rather than hiding it.

**Why is `staleDays` not always zero, then?**
Because the sweep runs once a day, and because it records when it *looked*. A two-day-old price is
disclosed as two days old rather than dressed up as live. That disclosure is the point: a price with
no timestamp is a claim, not a fact.

**Why did I get `verdict: "unknown"`?**
The database holds too little of that window to judge fairly — `verdictBasis` says how much was
missing. Try a longer `trendWindow`, or a more common product.

**Why does one product show only one retailer?**
Only one retailer in the database holds it. `retailerCount` is always the honest count, and
`savingPct` is 0 rather than an invented figure.

**Why is `cheapestFullBasket` null?**
No single retailer carries every item on your list. `bestCoverage` names who came closest.

**Can I re-price the same product later?**
Yes — keep `productRef`. It is stable and reveals nothing about where the observation came from.

**Do I pay for an add-on that found nothing?**
No. Add-ons are charged only when the lookup answered. Nutrition for a product that has none, or a
history window too sparse to judge, costs you nothing.

**Why is `size` missing on some products?**
Because no shop published one for that product. It is never computed — a size divided out of a
per-100g price returns 266.67g for a box printed 266g, and that is not the shop's own words.
`sizeKnown: false` says so plainly.

**`savingPct` looks huge — is it real?**
Check `size` and `comparable` first. A large gap between different-sized packs is not a saving, and
`comparable: false` means the two prices were seen days apart. Both fields are on every row so you
never have to take a percentage on trust.

**Is this hitting Coles and Woolworths when I run it?**
No — and that is why it is fast and why it can answer questions a live scrape cannot. Prices are
swept daily into a database that keeps every observation, so one call returns every retailer at once
plus years of history behind each. A live scraper can tell you today's price at one shop; it cannot
tell you that $3.50 is typical rather than a bargain.

# Actor input Schema

## `queries` (type: `array`):

One product name per line. Matched against product names in the database — this is not a live search of a supermarket site.

## `matchesPerQuery` (type: `integer`):

How many distinct products to price for each name. "milk" matches many products; "Weet-Bix 375g" matches one. Each product is charged separately, so raise this deliberately.

## `market` (type: `string`):

ISO 3166-1 alpha-2 country code. AU is the only market with coverage today; any other code returns no matches rather than guessing.

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

Hard cap on products priced across the whole run. Leave blank for no cap.

## `priceHistory` (type: `string`):

Adds a verdict (good price / typical / poor price) plus a history bar you can chart, by placing today's cheapest price in the range that product has actually held. Three months costs more than one, because it is a deeper lookup. Never charged when the database holds too little of the window to judge fairly.

## `unitPrice` (type: `boolean`):

The cheapest pack is often not the cheapest buy. This adds the price per 100g or 100ml at each retailer and says outright whether the cheapest pack is also the cheapest per unit. (+$0.004 per product)

## `listingHistory` (type: `boolean`):

Every listing ever seen for the product at every retailer — the link, when it was first seen, when it was last seen, how many times it has been observed, and whether it is still listed. (+$0.005 per product)

## `stockCodes` (type: `boolean`):

Each retailer's own SKU for the product, for matching against your catalogue or a supplier feed. (+$0.004 per product)

## `nutrition` (type: `boolean`):

Nutrition panel where the database holds one. Charged once per product name rather than per product, and coverage is genuinely patchy — products without it are marked so rather than returned empty. (+$0.01 per product name)

## Actor input object example

```json
{
  "queries": [
    "weet-bix",
    "tim tam",
    "vegemite"
  ],
  "matchesPerQuery": 3,
  "market": "AU",
  "priceHistory": "30d",
  "unitPrice": true,
  "listingHistory": false,
  "stockCodes": false,
  "nutrition": false
}
```

# Actor output Schema

## `products` (type: `string`):

Every product priced this run: cheapest and dearest retailer, the spread and saving, pack size, and the price-history verdict where that add-on ran.

## `basket` (type: `string`):

The whole list totalled per retailer, the cheapest single shop that carries all of it, and what that costs against buying each item wherever it is cheapest.

# 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 = {
    "queries": [
        "weet-bix",
        "tim tam",
        "vegemite"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gatherworks/au-grocery-price-comparison-history").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 = { "queries": [
        "weet-bix",
        "tim tam",
        "vegemite",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gatherworks/au-grocery-price-comparison-history").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 '{
  "queries": [
    "weet-bix",
    "tim tam",
    "vegemite"
  ]
}' |
apify call gatherworks/au-grocery-price-comparison-history --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gatherworks/au-grocery-price-comparison-history"
        }
    }
}

```

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/Ja9LgaiMK01Ezjuiw/builds/6gZyQETm9ei7RzzIl/openapi.json
