# RunGate — Cost Forecast & Budget Guard for Apify Tasks (`jernside/rungate-cost-guard`) Actor

Forecast Task cost and duration before execution, return ALLOW/WARN/BLOCK, and start approved runs with native Apify charge and timeout caps.

- **URL**: https://apify.com/jernside/rungate-cost-guard.md
- **Developed by:** [Demir](https://apify.com/jernside) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 guarded task runs

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

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

## What's an Apify Actor?

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

## RunGate — Cost Forecast & Budget Guard for Apify Tasks

RunGate is a preflight safety gate for saved Apify Tasks.

It reads recent Task runs, estimates the next run's cost and duration, checks the monthly budget, and returns one decision:

- **ALLOW** — the Task is within policy.
- **WARN** — history is weak or the Task configuration changed.
- **BLOCK** — the predicted cost, duration, or monthly budget is outside policy.

When a run is approved, RunGate starts the saved Task without replacing its input and applies Apify's native `maxTotalChargeUsd` and timeout caps.

> **Unofficial community Actor.** RunGate is not affiliated with, endorsed by, or maintained by Apify.

### Why use RunGate?

A schedule normally starts a scraper immediately:

```text
Schedule → Target Task → Cost
```

With RunGate:

```text
Schedule → RunGate → ALLOW / WARN / BLOCK → Target Task with native caps
```

The target Task's URLs, proxy configuration, page limits, and other saved input remain inside the Task. RunGate only needs the Task ID, a scoped token, and the safety policy.

### Five-minute setup

1. Save the scraper configuration as an Apify Task.
2. Open RunGate and paste the Task ID plus a scoped Apify token.
3. Keep **Monitor only** for the first run and review the decision.
4. Save the RunGate input as a Task, switch to **Automatic guard**, and move the schedule to the RunGate Task.

### Input

| Field | Purpose |
|---|---|
| Target Task ID | The saved Task protected by RunGate |
| Scoped Apify API token | Reads and starts only the permitted Task |
| Monthly Task budget | Monthly policy limit in USD |
| Maximum cost per run | Forecast threshold and native Apify charge cap |
| Maximum run duration | Forecast threshold and native timeout |
| Risk tolerance | Controls how weak history and Task changes are handled |

### Example result

```json
{
  "status": "ok",
  "decision": "BLOCK",
  "reasonCodes": ["PREDICTED_RUN_COST_EXCEEDS_LIMIT"],
  "recommendedAction": "The target Task will not start. Increase the relevant limit only after reviewing the cost or duration risk.",
  "predictedCostUsd": 6.42,
  "costUpperUsd": 8.11,
  "monthlySpendForecastUsd": 142.8,
  "targetRunStarted": false
}
```

### Forecasting method

V1 uses robust, explainable statistics:

- median
- recent weighted average
- median absolute deviation
- 10th and 90th percentiles
- current-month spend projection

TimesFM is intentionally not included in V1. It adds infrastructure cost before enough run history exists to justify it.

### Pay-per-event configuration

Create these exact events in **Publication → Monetization**:

| Event name | Suggested initial price | Trigger |
|---|---:|---|
| `cost-analysis` | $0.03 | Analysis completed without starting the target Task |
| `guarded-run` | $0.05 | An approved target Task was started with native caps |

RunGate calls only one custom event per run.

### Security

- The API token is marked `isSecret: true` and encrypted by Apify.
- The token is never logged, stored, hashed, or returned.
- Sensitive keys are redacted before the Task configuration fingerprint is created.
- RunGate does not read the target Task's dataset output.
- The target Task starts with a native cost cap and timeout.

See [SECURITY.md](SECURITY.md) for the threat model and reporting guidance.

### Product boundaries

RunGate does not:

- guarantee the final bill
- inspect or repair scraper code
- read the target dataset
- replace account-wide Apify spending limits
- provide a dashboard outside Apify
- require an external server or database

### Documentation

- [ARCHITECTURE.md](ARCHITECTURE.md) — system boundaries and state flow
- [DESIGN.md](DESIGN.md) — product and interaction design decisions
- [SECURITY.md](SECURITY.md) — threat model and controls
- [DEPLOYMENT.md](DEPLOYMENT.md) — build, monetization, and Store publishing
- [STORE\_LISTING.md](STORE_LISTING.md) — ready-to-paste Store copy

# Actor input Schema

## `targetTaskId` (type: `string`):

Paste the Task ID or username~task-name from the Task API tab. Example: your-username~daily-product-scraper.

## `apifyToken` (type: `string`):

Used only to read and start the target Task. The value is encrypted by Apify and never written to logs or output.

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

Monitor only analyzes the Task without starting it. Automatic guard starts only runs permitted by the policy.

## `monthlyBudgetUsd` (type: `number`):

Maximum monthly spend in USD for this target Task.

## `maxRunCostUsd` (type: `number`):

RunGate blocks a run when the predicted upper range exceeds this value. The same value is applied as Apify's native maximum-charge cap when a run starts.

## `maxDurationMinutes` (type: `integer`):

RunGate blocks historically longer jobs and applies this value as the target run timeout.

## `riskTolerance` (type: `string`):

Strict blocks weak history and Task changes. Balanced warns. Permissive keeps more runs eligible.

## `warnAction` (type: `string`):

Used only in Automatic guard mode. Blocking WARN results is the safest default.

## `lookbackRuns` (type: `integer`):

Number of recent target Task runs used for cost and duration estimates.

## `minHistoryRuns` (type: `integer`):

Minimum completed runs required before the estimate is considered established.

## Actor input object example

```json
{
  "targetTaskId": "your-username~daily-product-scraper",
  "mode": "monitor_only",
  "monthlyBudgetUsd": 100,
  "maxRunCostUsd": 5,
  "maxDurationMinutes": 60,
  "riskTolerance": "balanced",
  "warnAction": "block",
  "lookbackRuns": 30,
  "minHistoryRuns": 5
}
```

# Actor output Schema

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

No description

## `outputRecord` (type: `string`):

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("jernside/rungate-cost-guard").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("jernside/rungate-cost-guard").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 '{}' |
apify call jernside/rungate-cost-guard --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jernside/rungate-cost-guard"
        }
    }
}

```

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/xeGSE5FeemusXL7wb/builds/d6OmkrvX2W6TflGVZ/openapi.json
