Inngest

Docs › Flow control

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.

EventQueued for throttle capacityStep executingCompletedThrottle capacitySix events arrive 0.5s apart. Three runs (limit + burst) start at once, then one run starts every 3s (period ÷ limit). The rest wait in the queue; none are dropped.

Set a throttle on a function

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
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
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. If excess events should be skipped rather than queued, use 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.

Updated Nov 10, 2026

Powered byZunoy