# Staying Within Usage Limits

Your contract with Prismatic outlines fair use limits, typically measured in gigabyte-seconds of compute per instance per month. In this article we cover how we compute usage, and provide some recommendations we have for building efficient integrations.

## How usage is measured[​](#how-usage-is-measured "Direct link to How usage is measured")

When an instance's flow is invoked (either by [webhook](https://prismatic.io/docs/integrations/triggers/webhook.md), [schedule](https://prismatic.io/docs/integrations/triggers/schedule.md), via [AI/MCP](https://prismatic.io/docs/ai/model-context-protocol.md), or manually), the amount of time the flow takes to run is noted. At the end of the month, the total number of seconds an instance ran is multiplied by the amount of memory allocated to the instance to compute the instance's total gigabyte-seconds (GB-seconds). By default, an instance is allocated with 1GB of memory (though, you can [allocate more](https://prismatic.io/docs/integrations/integration-runner-environment-limits.md#memory-allocation) as needed).

Here are a couple of examples:

**Example 1:** An instance with the default instance profile receives an average of 2000 webhook requests each day. It takes about 3 seconds to process the request, transform the data, and send the data upstream. The instance consumes about 180,000 GB-s of compute each month.

`2000 executions/day x 3 seconds x 30 days = 180,000 GB-s`

**Example 2:** An instance runs on a schedule four times per day. It fetches a large CSV file, compares its contents to an upstream system, and syncs data upstream. Due to the size of the datasets, it runs with 8GB of memory to avoid OOM errors. On average, the flow takes 5 minutes (300 seconds) to run. The instance consumes about 288,000 GB-s of compute each month.

`4 executions/day x 300 seconds x 30 days x 8 GB = 288,000 GB-s`

## Find where usage is going[​](#find-where-usage-is-going "Direct link to Find where usage is going")

You can view your total execution compute usage by clicking your organization name on the bottom left of the web app, and then navigating to the **Utilization** tab.

You can find similar graphs for specific instances by navigating to an instance and opening the instance's **Utilization** tab.

If you'd like a regular report of instance compute usage, you can query [`instanceDailyUsageMetrics`](https://prismatic.io/docs/api/schema/queries.md#instancedailyusagemetrics) in the API to rank instances by `spendMbSecs`. An example of how to page over records and pull that data is available in our examples repo [in GitHub](https://github.com/prismatic-io/examples/tree/main/api/query-customer-usage).

Once you've found your most compute-intensive instances, examine some recent executions. Logs display the number of seconds each step took. Are there specific steps that take longer than they seem they should (e.g. a step that makes an HTTP request takes 30 seconds, etc).

## Building compute-efficient flows[​](#building-compute-efficient-flows "Direct link to Building compute-efficient flows")

Building compute-efficient workflows is critical for two reasons:

1. They ensure you stay within fair use limits
2. Data syncs faster between your app and the other apps or services your customers use

### Prefer webhooks over polling[​](#prefer-webhooks-over-polling "Direct link to Prefer webhooks over polling")

A [polling trigger](https://prismatic.io/docs/integrations/triggers/app-events.md#app-event-triggers-with-polling) runs an execution every time it checks for changes, whether or not anything changed. Even though an execution that finds no new data stops immediately, you still pay for the runner to start up and query the third-party API.

An instance polling every five minutes runs 8,640 times per month. At two seconds per check, that is over 17,000 GB-s per instance, per month - spent entirely on finding nothing:

`288 executions/day x 2 seconds x 30 days = 17,280 GB-s`

A [webhook-based app event trigger](https://prismatic.io/docs/integrations/triggers/app-events.md#app-event-triggers-with-webhooks) only runs when data actually changes. Many connectors register and tear down webhooks automatically when an instance is deployed or removed, so there is no extra work for your customers. If a connector does not support webhooks, you can build the subscription logic into a [deploy flow](https://prismatic.io/docs/integrations/triggers/management.md) or a [custom trigger](https://prismatic.io/docs/custom-connectors/triggers.md#app-event-webhook-triggers).

If polling is your only option, widen the interval and consider whether you need to poll around the clock. A cron [schedule](https://prismatic.io/docs/integrations/triggers/schedule.md) of `*/15 8-17 * * 1-5` costs about a tenth of a five-minute round-the-clock schedule.

Measure your empty polls

On an [executions](https://prismatic.io/docs/monitor-instances/executions.md) screen, click **Filter** and select **Exclude executions without trigger-detected changes** to see how many of your polling executions found work to do.

### Subscribe only to webhook events you care about[​](#subscribe-only-to-webhook-events-you-care-about "Direct link to Subscribe only to webhook events you care about")

When you do subscribe to webhooks, subscribe only to the events you care about. Most apps let you scope a subscription by event type and object type. Subscribing to every object change when you only need closed opportunities can multiply your executions many times over.

### Poll diffs over entire data sets[​](#poll-diffs-over-entire-data-sets "Direct link to Poll diffs over entire data sets")

When you fetch data on a schedule, request only the records that changed since your last run rather than re-fetching everything.

Save a cursor - usually an `updatedAt` timestamp or the ID of the last record you processed - to [flow state](https://prismatic.io/docs/integrations/persist-data.md) at the end of each execution, and use it to filter your next request. Built-in [polling triggers](https://prismatic.io/docs/integrations/triggers/app-events.md#app-event-triggers-with-polling) maintain this cursor for you.

Push the filter into the API request itself (`updated_since=`, a SOQL `WHERE` clause, an OData `$filter`) rather than fetching everything and discarding most of it in a code step. Fetching 50,000 records to find the 40 that changed costs you the full download, the memory to hold it, and the time to process it.

Consider a flow that syncs a 50,000-record dataset four times a day. A full sync takes four minutes; an incremental sync of a few dozen records takes five seconds:

```text
Full sync:        4 executions/day x 240 seconds x 30 days = 28,800 GB-s
Incremental sync: 4 executions/day x 5 seconds x 30 days   =    600 GB-s

```

While you are at it, request only the fields you need. Smaller payloads mean less memory, shorter executions, and smaller step results to serialize.

For the full pattern - an initial backfill followed by incremental updates - see [data sync guidelines](https://prismatic.io/docs/intro/guidelines/data-sync-guidelines.md) and [large data syncs](https://prismatic.io/docs/integrations/common-patterns/large-data-sync.md).

### Enable singleton executions[​](#enable-singleton-executions "Direct link to Enable singleton executions")

If a scheduled or polling flow takes longer to run than its schedule interval, a second execution starts while the first is still running.

That is expensive, because your cursor is not saved until an execution finishes. A flow that runs every five minutes but takes ten minutes to complete will start a second execution that fetches and processes the exact same records as the first. You pay twice for the same work, and you risk writing duplicate data upstream.

[Enable singleton executions](https://prismatic.io/docs/integrations/triggers/schedule.md#ensuring-singleton-executions-for-scheduled-flows) on your scheduled and polling triggers so that a new execution is skipped if one is already running. In a code-native integration, see [enabling singleton executions for code-native flows](https://prismatic.io/docs/integrations/code-native/flows.md#enabling-singleton-executions-for-code-native-flows).

For webhook-based flows, use [flow concurrency](https://prismatic.io/docs/integrations/triggers/fifo-queue.md) to throttle or serialize executions instead.

### Use memory efficiently when handling large files[​](#use-memory-efficiently-when-handling-large-files "Direct link to Use memory efficiently when handling large files")

Memory allocation multiplies the cost of an entire execution, not just the steps that need the memory. If you raise an instance to 8 GB so that one step can deserialize a large file, you pay 8 GB for every second of the run:

```text
1 GB instance, 5-minute execution: 300 seconds x 1 GB = 300 GB-s
8 GB instance, 5-minute execution: 300 seconds x 8 GB = 2,400 GB-s

```

So it is usually cheaper to reduce how much memory your flow needs than to allocate more of it.

Large files are the most common cause of memory pressure. An 80 MB CSV file can consume well over 1 GB by the time it has been downloaded as a buffer, converted to a string, persisted as a step result, and deserialized into JavaScript objects. Rather than holding the whole file in memory, process it as a [Node.js stream](https://prismatic.io/docs/custom-connectors/handling-large-files-in-custom-components.md) so that you load, process, and discard one chunk at a time. See [handling large files](https://prismatic.io/docs/integrations/common-patterns/large-files.md) for additional patterns.

A few other habits keep memory - and therefore cost - down:

* **Avoid oversized step results.** A [loop](https://prismatic.io/docs/components/loop.md) returns an array containing its final step's result from every iteration, so a 50 KB result across 10,000 iterations produces a 500 MB step result. End the loop with a code step that returns `{ data: null }`.
* **Keep logging proportionate.** Thousands of [log](https://prismatic.io/docs/monitor-instances/logging.md) lines inside a loop force the logger to serialize and commit each one. Log a summary per batch instead of a line per record.
* **Store cursors, not documents.** Keep [persisted data](https://prismatic.io/docs/integrations/persist-data.md) small - record IDs and timestamps rather than entire payloads.

[Memory management](https://prismatic.io/docs/integrations/memory-management.md) covers these scenarios in more detail, and you can enable [debug mode](https://prismatic.io/docs/integrations/troubleshooting.md#debug-mode) to see memory usage after each step.

### Leverage bulk APIs if available[​](#leverage-bulk-apis-if-available "Direct link to Leverage bulk APIs if available")

When you write records one at a time, network latency dominates your execution time. Five thousand records at 200 ms per request is nearly 17 minutes of compute - long enough to exceed the [15-minute execution limit](https://prismatic.io/docs/integrations/integration-runner-environment-limits.md#execution-time-limitations) as well. Sending the same records in batches of 500 takes ten requests and a few seconds.

Many APIs offer some form of bulk endpoint - `POST /records/batch`, Salesforce's Bulk API, a multi-row SQL insert. Prefer them over a [loop](https://prismatic.io/docs/integrations/low-code-integration-designer/looping.md) that makes one call per record.

The same applies when reading. Request the largest page size the API allows so that you [page through](https://prismatic.io/docs/integrations/common-patterns/loop-over-paginated-api.md) a dataset in fewer round trips.

If no bulk endpoint exists, [processing data in parallel](https://prismatic.io/docs/integrations/common-patterns/processing-data-in-parallel.md) within a single execution reduces the wall-clock time you spend waiting on the network, and therefore your GB-s.

Parallelism across flows shortens runs, but does not reduce compute

Fanning work out to sibling flows with the [cross-flow](https://prismatic.io/docs/integrations/triggers/cross-flow.md) trigger runs several executions at once, each with its own memory allocation. That helps you stay under the 15-minute execution limit, but the total GB-s stays roughly the same.

### Avoid sleeping within executions[​](#avoid-sleeping-within-executions "Direct link to Avoid sleeping within executions")

An execution that sleeps costs exactly as much as an execution that completes work. The [Sleep](https://prismatic.io/docs/components/sleep.md) component is convenient for waiting on a third-party job to finish, but polling a job's status every ten seconds for five minutes costs 300 GB-s per execution and produces nothing:

`100 executions/day x 300 seconds x 30 days = 900,000 GB-s`

Instead, end the execution and let something else restart the work:

* **Have the third-party app tell you when it is done.** Many APIs that kick off long-running jobs accept a callback URL. Point it at a [webhook trigger](https://prismatic.io/docs/integrations/triggers/webhook.md) on a second flow that processes the finished result.
* **Check status on a schedule.** Write the pending job ID to [cross-flow state](https://prismatic.io/docs/integrations/persist-data.md), and have a [scheduled](https://prismatic.io/docs/integrations/triggers/schedule.md) flow check outstanding jobs periodically and process the ones that have finished.
* **Throttle outside the execution, not inside it.** If you are sleeping to stay under a third-party rate limit, use [flow concurrency](https://prismatic.io/docs/integrations/triggers/fifo-queue.md) instead. Queued requests wait outside of an execution, so you are not billed while they sit in line.

## Related documentation[​](#related-documentation "Direct link to Related documentation")

* [Runner environment and limits](https://prismatic.io/docs/integrations/integration-runner-environment-limits.md) - memory, execution time, and payload constraints
* [Memory management](https://prismatic.io/docs/integrations/memory-management.md) - common out-of-memory scenarios and how to avoid them
* [Data sync guidelines](https://prismatic.io/docs/intro/guidelines/data-sync-guidelines.md) - backfill and incremental update patterns
* [Common patterns](https://prismatic.io/docs/integrations/common-patterns.md) - large data syncs, parallel processing, and file handling
