# Throttling

> Smooth traffic bursts by queueing runs until your service has capacity.

Protect an API that accepts only a fixed number of requests per period. Throttling queues runs above the start limit and starts them as capacity becomes available, so burst traffic does not discard work.

## Set a throttle on a function

```typescript {{ title: "TypeScript" }}
import { Inngest } from "inngest";

const inngest = new Inngest({ id: "my-app" });

export const summarize = inngest.createFunction(
  {
    id: "summarize",
    triggers: { event: "ai/summary.requested" },
    throttle: {
      limit: 1,
      period: "5s",
      burst: 2,
      key: "event.data.user_id",
    },
  },
  async ({ event, step }) => {
    return step.run("record-request", () => ({
      userId: String(event.data.user_id),
      textLength: String(event.data.text).length,
    }));
  },
);
```

```python {{ title: "Python" }}
import datetime

import inngest

inngest_client = inngest.Inngest(app_id="my-app")

@inngest_client.create_function(
    fn_id="summarize",
    trigger=inngest.TriggerEvent(event="ai/summary.requested"),
    throttle=inngest.Throttle(
        limit=1,
        period=datetime.timedelta(seconds=5),
        burst=2,
        key="event.data.user_id",
    ),
)
async def summarize(ctx: inngest.Context) -> dict[str, object]:
    async def record_request() -> dict[str, object]:
        return {
            "userId": str(ctx.event.data["user_id"]),
            "textLength": len(str(ctx.event.data["text"])),
        }

    return await ctx.step.run("record-request", record_request)
```

```go {{ title: "Go" }}
import (
	"context"
	"net/http"
	"time"

	"github.com/inngest/inngestgo"
	"github.com/inngest/inngestgo/step"
)

type SummaryRequested struct {
	UserID string `json:"user_id"`
	Text   string `json:"text"`
}

type RecordedRequest struct {
	UserID     string `json:"userId"`
	TextLength int    `json:"textLength"`
}

func main() {
	client, err := inngestgo.NewClient(inngestgo.ClientOpts{AppID: "my-app"})
	if err != nil {
		panic(err)
	}

	_, err = inngestgo.CreateFunction(
		client,
		inngestgo.FunctionOpts{
			ID: "summarize",
			Throttle: &inngestgo.ConfigThrottle{
				Limit:  1,
				Period: 5 * time.Second,
				Burst:  2,
				Key:    inngestgo.StrPtr("event.data.user_id"),
			},
		},
		inngestgo.EventTrigger("ai/summary.requested", nil),
		func(ctx context.Context, input inngestgo.Input[SummaryRequested]) (any, error) {
			return step.Run(ctx, "record-request", func(ctx context.Context) (RecordedRequest, error) {
				return RecordedRequest{
					UserID:     input.Event.Data.UserID,
					TextLength: len(input.Event.Data.Text),
				}, nil
			})
		},
	)
	if err != nil {
		panic(err)
	}

	_ = http.ListenAndServe(":8080", client.Serve())
}
```

This function sets a separate throttle for each `user_id`. Replace the example step with the service call you need to pace. Inngest evaluates `key` against the triggering event. Without a key, the throttle applies to all runs of this function. Each function has its own throttle, even when two functions use the same key.

## Choose the settings

- `limit` sets the number of runs allowed to start during `period`.
- `period` accepts 1 second through 7 days, with one-second granularity.
- `burst` allows additional run starts in a short burst on top of `limit`. Within a period, at most `limit + burst` runs may start.
- `key` is an optional expression. Each distinct result gets its own limit.

Inngest spreads run starts over time and starts queued runs in first-in, first-out order. When the throttle has no capacity, the run waits in the queue; it is not discarded. A large backlog can outlive the time in which the work is useful, so set a start timeout when delayed runs should expire.

## Pick the right control

Throttling limits **new run starts**, not the steps inside a run. If each run makes several requests to a provider, account for those requests when choosing the start rate. For a cap on executing steps, use [Step concurrency](/docs-markdown/durable-execution/flow-control/concurrency). If excess events should be skipped rather than queued, use [Rate limiting](/docs-markdown/durable-execution/flow-control/rate-limiting).

## Check the result

Send several events with the same `user_id`, then inspect their run start times in the Inngest dashboard. Send an event with a different `user_id` to see the independent keyed limit. If starts lag longer than expected, check the backlog, `burst`, and any start timeout.