# Cumulative Spending Monitor (`tri_angle/cumulative-spending-monitor`) Actor

- **URL**: https://apify.com/tri\_angle/cumulative-spending-monitor.md
- **Developed by:** [Tri⟁angle](https://apify.com/tri_angle) (community)
- **Categories:** Other
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Cumulative Spending Monitor

Monitor your Apify spending in real-time and get instant notifications when your usage exceeds predefined thresholds.

### What Does This Actor Do?

This Actor helps you keep your Apify costs under control by monitoring spending across your Actors and alerting you when you exceed budget limits. Whether you're running a single critical Actor or managing multiple automations, this tool ensures you're always aware of your spending patterns.

### Key Features

- 🔍 **Real-time Monitoring**: Track cumulative spending across all your Actors or specific ones you choose
- ⏰ **Flexible Time Periods**: Monitor billing cycles or custom 24-hour periods starting at any time
- 💰 **Custom Thresholds**: Set spending limits in USD for each monitoring period
- 📧 **Multi-Channel Notifications**: Get alerts via Slack, email, or both when thresholds are exceeded
- 🔕 **Smart Alert System**: Prevents notification spam by remembering when alerts were already sent

### Required Permissions

🔑 **This Actor requires full account permissions to function properly.**

To monitor your spending, the Actor needs to access information about Actor runs in your account. This includes reading run details, usage statistics, and billing information through the Apify API.

**Why full permissions?**

- The Actor queries all runs (or runs for specific Actors) that started within your monitoring periods
- It retrieves usage data and costs associated with each run
- It accesses your billing cycle information when monitoring billing periods

**Your data is safe:** The Actor only reads data to calculate spending—it doesn't modify any of your Actors, runs, or account settings. All operations are read-only except for storing notification timestamps in its own key-value store.

### How It Works

1. **Configure Your Periods**: Define when you want to monitor spending (e.g., your billing cycle, or daily periods starting at 9 AM)
2. **Set Spending Limits**: Specify how much you're willing to spend in USD for each period
3. **Choose Your Actors**: Monitor all Actors or select specific ones you want to track
4. **Get Notified**: Receive alerts via Slack or email when spending exceeds your thresholds

The Actor fetches data from the Apify API, calculates cumulative spending for each period, and compares it against your thresholds. When a limit is reached, you'll be notified immediately so you can take action.

### Configuration

#### Time Periods

You can monitor spending across multiple time periods simultaneously:

- **Billing Cycle**: Use `"billing"` to monitor your monthly Apify billing period
- **Custom 24-Hour Periods**: Use time in 24-hour format (e.g., `"09:00"`, `"18:30"`) to monitor rolling 24-hour windows. Times are interpreted in UTC.

Each period needs:

- A **label** to identify it (e.g., "Daily 9-9", "Billing Cycle")
- A **period** definition (`"billing"` or time like `"09:00"`)
- An optional **spending threshold** in USD

#### Actor Selection

- Leave the "Actors" field empty to monitor **all Actors** in your account
- Or specify Actor IDs or username/name combinations to monitor only specific Actors

#### Notifications

**Slack Notifications:**

- Enable Slack notifications in the configuration
- Provide your Slack bot token (keep it secret!)
- Specify one or more channels where alerts should be posted

**Email Notifications:**

- Enable email notifications in the configuration
- Provide comma-separated lists of email addresses
- Each list is treated as a separate group (recipients in the same group will see each other)
- You'll receive a formatted table showing periods that exceeded thresholds

#### Preventing Repeated Notifications

To avoid getting the same alert multiple times, provide an **Instance Name**. This name helps the Actor remember which thresholds have already triggered notifications.

**How it works:**

- The Actor stores notification records in a named key-value store using your instance name
- Each record is tied to a specific period (identified by its label and exact start/end times)
- When a threshold is exceeded, the Actor checks if a notification was already sent for that period
- If yes, it skips sending another notification
- If no, it sends the notification and records the timestamp

**Example:** If you set up daily monitoring from 9 AM to 9 AM and the Actor runs every hour, you'll only get one notification per day when the threshold is first exceeded—not 24 notifications!

**Important:** The same period (same label and time range) won't trigger duplicate notifications. If you change the period times or create a new period, it will be treated as a new monitoring window and can send notifications again.

#### Instance Name Best Practices

Choose a unique, descriptive instance name:

- Use names like `"production-monitor"` or `"dev-spending-tracker"`
- Don't change the instance name unless you want to reset notification history
- Different Actors with different purposes should use different instance names

### Output

The Actor saves detailed spending reports to its dataset, including:

- Period label, start time, and end time
- Total spending in USD for the period
- Number of runs analyzed
- Spending breakdown by Actor
- Whether the threshold was exceeded and when
- Timestamp of the last notification sent (if any)

### Limitations

⚠️ **Important:** This Actor only considers **runs that started** within the monitored period.

**Why?** The Apify API filters runs by their start time (`startedAfter` parameter) and doesn't provide more granular filtering options.

**What this means:**

- A run that started **before** your monitoring period but finished **during** it will **not** be counted
- A run that started **during** your monitoring period will be counted, even if it finishes after the period ends

**Example:** If you're monitoring spending from 9 AM to 9 AM the next day, and a long-running Actor started at 8 AM and finished at 10 AM, its costs won't be included in your 9-to-9 period report.

### Use Cases

- **Budget Control**: Ensure you never exceed your monthly cloud budget
- **Cost Optimization**: Identify which Actors consume the most resources
- **Development Safety**: Set low thresholds for test environments to catch runaway processes
- **Client Billing**: Monitor per-client Actor spending for accurate invoicing
- **Team Alerts**: Keep your whole team informed when automation costs spike

### Tips

- Start with conservative thresholds and adjust based on your typical usage patterns
- Monitor both your billing cycle and shorter daily periods for comprehensive coverage
- Use multiple notification channels to ensure critical alerts aren't missed
- Set up the Actor to run frequently (e.g., every hour) for near real-time monitoring
- Review the dataset output regularly to understand your spending trends

### Need Help?

If you have questions or need assistance setting up monitoring for your specific use case, reach out to Apify support or check the [Apify documentation](https://docs.apify.com).

---

**Stay in control of your automation costs with Cumulative Spending Monitor!** 🚀

# Actor input Schema

## `periods` (type: `array`):

Define one or more time periods to monitor spending. Each period can have its own spending threshold and will be tracked independently.
## `actors` (type: `array`):

List of specific Actors to monitor (use Actor IDs or username/name format like "apify/web-scraper"). Leave empty to monitor ALL Actors in your account.
## `instanceName` (type: `string`):

A unique name for this monitoring setup (e.g., "production-monitor"). This prevents duplicate notifications—once a threshold is reached for a period, you won't be notified again for that same period, even if the Actor runs multiple times. Choose a descriptive name and keep it consistent between runs.
## `enableSlackNotifications` (type: `boolean`):

Turn on to receive Slack messages when any monitored period exceeds its spending threshold.
## `slackToken` (type: `string`):

Your Slack bot token (starts with xoxb-). Required if Slack notifications are enabled. Keep this secret! Get one by creating a Slack App with chat:write permissions.
## `slackChannels` (type: `array`):

List of Slack channel names or IDs to post notifications to (e.g., #spending-alerts, C1234567890). Required if Slack notifications are enabled. Your bot must be added to these channels.
## `enableEmailNotifications` (type: `boolean`):

Turn on to receive email messages when any monitored period exceeds its spending threshold.
## `emailRecipients` (type: `array`):

Comma-separated email addresses for each notification group. Each entry creates a separate email, and recipients in the same entry can see each other. Example: "user1@example.com,user2@example.com" (one group) or separate entries for individual emails. Required if email notifications are enabled.

## Actor input object example

```json
{
  "enableSlackNotifications": false,
  "enableEmailNotifications": false
}
````

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("tri_angle/cumulative-spending-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("tri_angle/cumulative-spending-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 '{}' |
apify call tri_angle/cumulative-spending-monitor --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Cumulative Spending Monitor",
        "version": "0.0",
        "x-build-id": "UdEBSIqabzmOU97Dy"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/tri_angle~cumulative-spending-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-tri_angle-cumulative-spending-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/tri_angle~cumulative-spending-monitor/runs": {
            "post": {
                "operationId": "runs-sync-tri_angle-cumulative-spending-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/tri_angle~cumulative-spending-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-tri_angle-cumulative-spending-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",
                "required": [
                    "periods"
                ],
                "properties": {
                    "periods": {
                        "title": "Monitoring Periods",
                        "minItems": 1,
                        "type": "array",
                        "description": "Define one or more time periods to monitor spending. Each period can have its own spending threshold and will be tracked independently.",
                        "items": {
                            "type": "object",
                            "properties": {
                                "label": {
                                    "title": "Period Label",
                                    "description": "A friendly name to identify this period in reports and notifications (e.g., \"Daily Morning Check\", \"Monthly Billing\").",
                                    "type": "string",
                                    "editor": "textfield"
                                },
                                "period": {
                                    "title": "Time Period",
                                    "description": "Specify \"billing\" to track your Apify billing cycle, or use 24-hour time format (e.g., \"09:00\", \"18:30\") to monitor a rolling 24-hour window starting at that time (UTC). Each time-based period covers 24 hours from the specified start time.",
                                    "type": "string",
                                    "editor": "textfield",
                                    "pattern": "^(([0-1][0-9]|2[0-3]):[0-5][0-9]|billing)$"
                                },
                                "thresholdUsd": {
                                    "title": "Spending Threshold (USD)",
                                    "description": "The maximum amount you want to spend during this period, in US dollars. When cumulative spending exceeds this amount, you'll receive a notification. Leave empty to monitor without alerting.",
                                    "type": "number",
                                    "editor": "number",
                                    "minimum": 0
                                }
                            },
                            "required": [
                                "label",
                                "period"
                            ]
                        }
                    },
                    "actors": {
                        "title": "Actors to Monitor",
                        "type": "array",
                        "description": "List of specific Actors to monitor (use Actor IDs or username/name format like \"apify/web-scraper\"). Leave empty to monitor ALL Actors in your account.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "instanceName": {
                        "title": "Instance Name",
                        "type": "string",
                        "description": "A unique name for this monitoring setup (e.g., \"production-monitor\"). This prevents duplicate notifications—once a threshold is reached for a period, you won't be notified again for that same period, even if the Actor runs multiple times. Choose a descriptive name and keep it consistent between runs."
                    },
                    "enableSlackNotifications": {
                        "title": "Enable Slack Notifications",
                        "type": "boolean",
                        "description": "Turn on to receive Slack messages when any monitored period exceeds its spending threshold.",
                        "default": false
                    },
                    "slackToken": {
                        "title": "Slack Bot Token",
                        "type": "string",
                        "description": "Your Slack bot token (starts with xoxb-). Required if Slack notifications are enabled. Keep this secret! Get one by creating a Slack App with chat:write permissions."
                    },
                    "slackChannels": {
                        "title": "Slack Channels",
                        "minItems": 1,
                        "type": "array",
                        "description": "List of Slack channel names or IDs to post notifications to (e.g., #spending-alerts, C1234567890). Required if Slack notifications are enabled. Your bot must be added to these channels.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "enableEmailNotifications": {
                        "title": "Enable Email Notifications",
                        "type": "boolean",
                        "description": "Turn on to receive email messages when any monitored period exceeds its spending threshold.",
                        "default": false
                    },
                    "emailRecipients": {
                        "title": "Email Recipients",
                        "minItems": 1,
                        "type": "array",
                        "description": "Comma-separated email addresses for each notification group. Each entry creates a separate email, and recipients in the same entry can see each other. Example: \"user1@example.com,user2@example.com\" (one group) or separate entries for individual emails. Required if email notifications are enabled.",
                        "items": {
                            "type": "string",
                            "pattern": "^[^@\\s,]+@[^@\\s,]+(,[^@\\s,]+@[^@\\s,]+)*$"
                        }
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
