Inngest step return 4 MiB limit: return the Sume artifact URL
Inngest caps one step return at 4 MiB and run state at 32 MiB. Return the media.sume.com artifact URL from step.run instead of the video bytes.

A step in an Inngest function can return at most 4 MiB, so never return a rendered video from step.run. Return the Sume artifact URL, a string of a few hundred bytes, and let the next step or the consumer fetch the file from media.sume.com when needed.
Limits are from the Inngest usage limits page, and job behavior from Sume's Core workflow and Jobs and results, read 2026-09-30.
What are the Inngest limits?
The page lists the step return cap and the run state cap, and gives the same advice for both: keep large data outside Inngest state.
| Limit | Value | Guidance on the page |
|---|---|---|
| Step return | 4 MiB returned by one step | Store large results outside step state and return a reference |
| Function run state | 32 MiB across event data, step data, return data, metadata | Keep large data outside run state |
| Event size | 256 KiB to 3 MiB depending on plan | Not stated on the page; keep payloads small and pass references |
What does a completed Sume job give me?
A completed job carries result.artifacts[] entries with id, url, type and content_type. The docs say Sume-owned artifact URLs under https://media.sume.com are the public contract, and that integrations should store the Sume URL rather than raw provider URLs. That URL is the reference Inngest asks you to return.
How do I avoid polling inside steps?
Submit with mode: "webhook" and wait for the event. Sume sends terminal job events only, and results are available only after completion, so a step should not fetch the result before the event arrives. The pattern is covered in Inngest: wait for an AI video webhook.
// inside an Inngest function
const url = await step.run("get-artifact-url", async () => {
const res = await fetch("https://api.sume.com/v1/jobs/" + jobId + "/result", {
headers: { Authorization: "Bearer " + process.env.SUME_API_KEY },
});
const body = await res.json();
const job = body.data ?? body; // the OpenAPI schema wraps the payload in data
return job.result.artifacts[0].url; // a string, not the video
});What should the next step do with the URL?
Pass the URL to whatever needs the file: a publish call, a Timeline input, a CMS field. If a step genuinely must read the bytes, stream them to your own storage inside that step and return only the new reference, so neither the step return nor the 32 MiB run state grows with video size.
Sources
Related posts
More in Developers
- Instagram Graph API hashtag search: 30 per 7 days, and Sume
Meta caps hashtag search at 30 unique hashtags per account per rolling 7 days and needs app review. Sume's instagram_search_hashtag is a bounded public read.
- Instagram oEmbed: embed HTML vs Sume's instagram_media read
Instagram oEmbed is meant only for embedding, at up to 1,000 requests per hour. Sume's instagram_media returns media candidates for a post or reel URL.
- Lambda 90-minute timeout: does it change AI video jobs?
AWS raised Lambda's async timeout to 90 minutes on Managed Instances. A Sume job still fits best as submit, then poll or webhook, and sync waits stay 30 s.
- LangGraph interrupt response_schema for paid video approval
LangGraph 1.2.12 adds response_schema to interrupt(). Use a typed approve, edit or reject reply with max spend to drive a Sume dry_run, then submit.
Written by Sume