# MAP Violation Monitor — Price Floor Compliance Scraper (`studio-amba/map-violation-monitor`) Actor

Monitor MAP (Minimum Advertised Price) compliance for your products across European price-comparison sites. Runs Geizhals, Ceneo and Prisjakt by default (Idealo, Heureka, Skroutz optional), flags offers advertised below your floor price, and tracks new vs. recurring violations run over run.

- **URL**: https://apify.com/studio-amba/map-violation-monitor.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** E-commerce, Business
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 result scrapeds

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

## MAP Violation Monitor — Price Floor Compliance Scraper

Find **Minimum Advertised Price (MAP) violations** for your products across European price-comparison sites in one run. Give the actor a product list with a floor price per product, and it returns only the offers advertised **below** that floor — which engine found it, the market, how far below floor, and whether it is new or a violation that is still ongoing since your last run.

Brands and distributors pay agencies and manual "pige" teams to watch this. MAP Violation Monitor runs the check on a schedule instead.

### What it does

- **Runs our own price-comparison scrapers in parallel.** Geizhals (AT/DE) by default — the only one of the six confirmed working end-to-end on a plain residential proxy. Ceneo (PL), Prisjakt (SE), Idealo (DE), Heureka (CZ) and Skroutz (GR) are all available opt-in; see the reliability table below before adding them.
- **Matches each engine's result back to your product.** EAN/barcode match when available (Ceneo exposes a barcode field), fuzzy product-name match otherwise.
- **Converts every price to EUR** using ECB daily reference rates, so a floor price in EUR can be checked against an offer in PLN or SEK without you doing the conversion.
- **Returns only violations.** No noise — one row per matched offer that sits below your floor price, with the exact percentage below floor.
- **New-vs-recurring delta.** Schedule the actor daily and each violation is flagged `isNew` the first time it appears, then `false` on every following run while it stays active — so you can see which resellers keep re-offending.

### How to scrape MAP violation data across price-comparison sites

1. Enter your **products** — each one needs a `name` (used as the search query on every engine) and a `floorPrice`. Add `ean` if you have it and `currency` if it isn't EUR.
2. Pick your **engines**. Geizhals is the cloud-verified reliable default. Add Ceneo, Prisjakt, Idealo, Heureka or Skroutz for broader coverage — see the reliability table below before you do; several currently return nothing.
3. Run it. Each row is one violation: product, engine, market, advertised price, floor price, and how far below floor it sits.
4. Schedule it daily or weekly. The new-vs-recurring delta tells you which violations are fresh and which resellers haven't corrected their pricing.

### Honest limitation: no per-seller data

None of the six bundled engines expose a per-seller/per-merchant breakdown. Each one returns a **single aggregate "lowest price found"** per product page, plus (on most of them) a count of how many resellers that price was drawn from. There is no seller name, no per-offer list.

So `sellerOrSite` in the output is honestly **the comparison engine itself** (e.g. "Geizhals"), not a named reseller. `offerCount` tells you how many resellers that aggregate price was pulled from, where the engine exposes it. If you need reseller-level attribution, you would need a scraper built against the individual retailer sites, not a price-comparison aggregator — that's a different (and much larger) build than this one.

This is a deliberate, disclosed limitation, not a bug: price-comparison engines are the fastest way to catch that a violation exists across a market; they were never going to tell you which of the 12 retailers listed on them did it.

### Engines and reliability (method disclosure)

Cloud-verified Jul 2026 — every engine below was actually run, not just read from its source code.

| Engine | Market | Currency | Method | Reliability | Notes |
|---|---|---|---|---|---|
| **Geizhals** | AT/DE | EUR | Residential proxy + parsing | High | Cloud-verified working: real offers, real matches, real violations. Default. |
| **Ceneo** | PL | PLN | Residential proxy + parsing | Broken (child bug) | Also exposes EAN — the only engine that does — but its own detail-page parser is currently failing on live markup ("No product name on ..." warnings): it finds the listing, extracts nothing. Opt-in until healed. |
| **Prisjakt** | SE | SEK | Residential proxy + parsing | Blocked | Serves a Cloudflare "Just a moment..." JS challenge on its search endpoint that a non-browser (Cheerio) crawler can't solve, even through a residential Swedish IP. Opt-in. |
| **Idealo** | DE | EUR | Bright Data Scraping Browser | Low | Hard-blocked on standard Web Unlocker as of Jul 2026; needs a live Scraping Browser CDP endpoint configured on the child actor. Opt-in. |
| **Heureka** | CZ | CZK | Bright Data Web Unlocker | Medium | DataDome-class protection; needs a Bright Data API key configured on the child actor. Opt-in. |
| **Skroutz** | GR | EUR | Bright Data Web Unlocker | Medium | Cloudflare "Just a moment" challenge even on Greek residential IPs; needs Bright Data. Opt-in. |

All six children get a RESIDENTIAL proxy scoped to their market by default — none of them work reliably on a plain/datacenter proxy, including the three with no explicit anti-bot code path. The actor degrades gracefully: if an engine fails or returns nothing for a product, the run continues with the others and the run summary records which engines succeeded and which failed.

### Documented limits

- **No per-seller attribution.** See above — output is engine-level, not merchant-level.
- **Query is by product name, not EAN.** None of the six engines support searching by barcode directly, so every product is searched by its `name`. EAN is only used to improve matching *after* results come back, and only on the one engine that exposes a barcode field (Ceneo).
- **Fuzzy name matching can miss or mismatch.** A very short or generic product name (e.g. just a brand) risks a false match on a different SKU. Use a specific model name and, where possible, tune `matchThreshold` up for stricter matching or down for wider recall.
- **FX rates are daily ECB reference rates**, not live market rates — fine for catching a MAP violation, not for accounting.
- **5 of the 6 engines are currently opt-in, not default, for real cloud-verified reasons** — a broken child parser (Ceneo), a Cloudflare JS challenge (Prisjakt), or a Bright Data dependency (Idealo/Heureka/Skroutz). Only Geizhals is the default. Adding the others is safe (the run degrades gracefully) but don't expect data from them until they're healed/upgraded.

### Output fields

Each row is one violation:

- `productName`, `ean`, `floorPrice`, `floorCurrency` — the product you're monitoring
- `engine`, `market`, `sellerOrSite`, `offerCount` — where the violation was found
- `advertisedPrice`, `currency`, `advertisedPriceEur`, `floorPriceEur`, `pctBelowFloor` — the pricing detail
- `matchType`, `matchScore` — how confident the match to your product is (`ean` = high confidence, `name` = fuzzy)
- `url` — link to the offending listing
- `isNew`, `firstSeenAt`, `stillActive` — the run-over-run delta
- `scrapedAt` — when this run happened

### Example output

```json
{
  "productName": "Sony WH-1000XM5",
  "ean": null,
  "floorPrice": 320,
  "floorCurrency": "EUR",
  "market": "AT/DE",
  "engine": "geizhals",
  "sellerOrSite": "Geizhals",
  "offerCount": 24,
  "advertisedPrice": 279,
  "currency": "EUR",
  "advertisedPriceEur": 279,
  "floorPriceEur": 320,
  "pctBelowFloor": 12.8,
  "matchType": "name",
  "matchScore": 0.75,
  "url": "https://geizhals.at/...",
  "isNew": true,
  "firstSeenAt": "2026-07-14T10:00:00.000Z",
  "stillActive": true,
  "scrapedAt": "2026-07-14T10:00:00.000Z"
}
````

### How matching works

1. **EAN match, when available.** If your product has an `ean` and an engine result exposes one (currently Ceneo only), an exact barcode match is used and scored at full confidence.
2. **Fuzzy name match, otherwise.** Product names are normalized (lowercased, punctuation stripped) and compared as word sets — the overlap ratio is the match score. The best-scoring result above `matchThreshold` (default 0.4) is used.
3. **No match above threshold means no row.** A product not found on an engine, or found but not confidently matched, produces neither a violation row nor an error — it's simply absent from the output for that engine.

A run with zero violations because nothing was below floor is a **success**, not a failure. The actor only fails when every engine call returned zero usable offers — a real infrastructure problem, not an empty result.

### Who uses this

- **Brands and distributors** enforcing MAP agreements with resellers across EU markets.
- **Channel managers** who need to know which reseller is undercutting the floor before a manual audit.
- **Pricing and revenue teams** tracking whether a competitor's grey-market inventory is leaking below agreed pricing.

### Pricing

Pay per result. You pay for the run start and for each violation row returned — that row is the entire deliverable, so there's no separate "raw data" tier. See the pricing tab for current rates.

### FAQ

**Can I monitor a product across multiple currencies?**
Yes. Set `floorPrice` and `currency` once per product; the actor converts every engine's local price to EUR using the same day's ECB rate before comparing, so a EUR floor is checked correctly against PLN and SEK offers.

**Does it tell me which specific retailer is violating?**
No — see the "Honest limitation" section above. It tells you which comparison engine and market the violation was found on, and how many resellers that aggregate price was drawn from where the engine exposes a count.

**How do I get a feed of only new violations?**
Schedule the actor and filter your downstream consumer on `isNew: true`. The per-account key-value store remembers what was seen on the previous run.

**Do I need a proxy?**
No — the actor defaults every engine to a RESIDENTIAL proxy scoped to its market automatically. You only need to supply your own `proxyConfiguration` if you want to override that (e.g. use a different proxy group). Note that a proxy alone won't fix Ceneo's current parser bug or Prisjakt's Cloudflare challenge, and Idealo/Heureka/Skroutz additionally need their own Bright Data configuration.

**Is this legal?**
The actor collects publicly advertised prices from public comparison sites. You are responsible for how you use the output, including any contractual enforcement action against resellers.

# Actor input Schema

## `products` (type: `array`):

List of products to check. Each product needs a name (used as the search query on every price-comparison engine) and a floorPrice (your Minimum Advertised Price, in the given currency). ean is optional and only improves matching on engines that expose a barcode field (currently Ceneo). currency defaults to EUR.

## `engines` (type: `array`):

Which price-comparison engines to run. Geizhals (AT/DE) is the only one confirmed working end-to-end on a plain residential proxy (cloud-verified) and is the default. Ceneo (PL) has a currently-broken detail-page parser (finds listings, extracts no data). Prisjakt (SE) serves a Cloudflare JS challenge that blocks a non-browser crawler even on a residential IP. Idealo (DE), Heureka (CZ) and Skroutz (GR) all need a Bright Data key or Scraping Browser endpoint configured on that child actor. All five are opt-in — add them for broader coverage, accepting a real chance of that engine returning nothing on a given run until it's healed/upgraded.

## `matchThreshold` (type: `number`):

Minimum fuzzy name-similarity score (0-1) required to match a product to an engine's search result when no EAN match is available. Lower catches more offers but risks matching the wrong product.

## `maxItemsPerSource` (type: `integer`):

Cap on search results pulled from each engine per product before matching.

## `timeoutPerSourceSecs` (type: `integer`):

How long to wait for each engine's child-actor run before treating it as failed.

## `proxyConfiguration` (type: `object`):

Passed through to the child price-comparison scrapers, overriding the actor's own per-engine default (a RESIDENTIAL proxy scoped to each engine's market — all 6 engines return 403/empty on a plain or datacenter proxy). Leave empty to use the built-in per-engine residential defaults.

## Actor input object example

```json
{
  "products": [
    {
      "name": "Sony WH-1000XM5",
      "floorPrice": 320,
      "currency": "EUR"
    }
  ],
  "engines": [
    "geizhals"
  ],
  "matchThreshold": 0.3,
  "maxItemsPerSource": 8,
  "timeoutPerSourceSecs": 150
}
```

# 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 = {
    "products": [
        {
            "name": "Sony WH-1000XM5",
            "floorPrice": 320,
            "currency": "EUR"
        }
    ],
    "engines": [
        "geizhals"
    ],
    "matchThreshold": 0.3,
    "maxItemsPerSource": 8,
    "timeoutPerSourceSecs": 150
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/map-violation-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 = {
    "products": [{
            "name": "Sony WH-1000XM5",
            "floorPrice": 320,
            "currency": "EUR",
        }],
    "engines": ["geizhals"],
    "matchThreshold": 0.3,
    "maxItemsPerSource": 8,
    "timeoutPerSourceSecs": 150,
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/map-violation-monitor").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 '{
  "products": [
    {
      "name": "Sony WH-1000XM5",
      "floorPrice": 320,
      "currency": "EUR"
    }
  ],
  "engines": [
    "geizhals"
  ],
  "matchThreshold": 0.3,
  "maxItemsPerSource": 8,
  "timeoutPerSourceSecs": 150
}' |
apify call studio-amba/map-violation-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=studio-amba/map-violation-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "MAP Violation Monitor — Price Floor Compliance Scraper",
        "description": "Monitor MAP (Minimum Advertised Price) compliance for your products across European price-comparison sites. Runs Geizhals, Ceneo and Prisjakt by default (Idealo, Heureka, Skroutz optional), flags offers advertised below your floor price, and tracks new vs. recurring violations run over run.",
        "version": "0.1",
        "x-build-id": "QVWceKcQvG4MRG49v"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/studio-amba~map-violation-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-studio-amba-map-violation-monitor",
                "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/studio-amba~map-violation-monitor/runs": {
            "post": {
                "operationId": "runs-sync-studio-amba-map-violation-monitor",
                "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/studio-amba~map-violation-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-studio-amba-map-violation-monitor",
                "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",
                "properties": {
                    "products": {
                        "title": "Products to monitor",
                        "type": "array",
                        "description": "List of products to check. Each product needs a name (used as the search query on every price-comparison engine) and a floorPrice (your Minimum Advertised Price, in the given currency). ean is optional and only improves matching on engines that expose a barcode field (currently Ceneo). currency defaults to EUR.",
                        "default": [
                            {
                                "name": "Sony WH-1000XM5",
                                "floorPrice": 320,
                                "currency": "EUR"
                            }
                        ]
                    },
                    "engines": {
                        "title": "Price-comparison engines",
                        "type": "array",
                        "description": "Which price-comparison engines to run. Geizhals (AT/DE) is the only one confirmed working end-to-end on a plain residential proxy (cloud-verified) and is the default. Ceneo (PL) has a currently-broken detail-page parser (finds listings, extracts no data). Prisjakt (SE) serves a Cloudflare JS challenge that blocks a non-browser crawler even on a residential IP. Idealo (DE), Heureka (CZ) and Skroutz (GR) all need a Bright Data key or Scraping Browser endpoint configured on that child actor. All five are opt-in — add them for broader coverage, accepting a real chance of that engine returning nothing on a given run until it's healed/upgraded.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "geizhals",
                                "ceneo",
                                "prisjakt",
                                "idealo",
                                "heureka",
                                "skroutz"
                            ],
                            "enumTitles": [
                                "Geizhals (AT/DE)",
                                "Ceneo (PL)",
                                "Prisjakt (SE)",
                                "Idealo (DE)",
                                "Heureka (CZ)",
                                "Skroutz (GR)"
                            ]
                        },
                        "default": [
                            "geizhals"
                        ]
                    },
                    "matchThreshold": {
                        "title": "Name match threshold",
                        "type": "number",
                        "description": "Minimum fuzzy name-similarity score (0-1) required to match a product to an engine's search result when no EAN match is available. Lower catches more offers but risks matching the wrong product.",
                        "default": 0.3
                    },
                    "maxItemsPerSource": {
                        "title": "Max results per engine per product",
                        "type": "integer",
                        "description": "Cap on search results pulled from each engine per product before matching.",
                        "default": 8
                    },
                    "timeoutPerSourceSecs": {
                        "title": "Per-engine timeout (seconds)",
                        "type": "integer",
                        "description": "How long to wait for each engine's child-actor run before treating it as failed.",
                        "default": 150
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Passed through to the child price-comparison scrapers, overriding the actor's own per-engine default (a RESIDENTIAL proxy scoped to each engine's market — all 6 engines return 403/empty on a plain or datacenter proxy). Leave empty to use the built-in per-engine residential defaults."
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
