# Hosted functions (https://upapi.io/docs/platform/hosted-functions)



A hosted function is code you write, deployed to upAPI and invoked exactly like
a marketplace operation: a `POST` to its slug with your API key. It runs in a
V8 isolate — not a container — so cold starts are milliseconds rather than
seconds.

This is the escape hatch for the glue you would otherwise host yourself: reshape
an operation's output, join two calls into one round trip, or wrap a private API
of your own so the rest of your system only ever talks to upAPI.

<Callout title="Hosted functions require a paid plan">
  The Free tier's allowance is zero, and the gateway refuses to run a function with `403 FORBIDDEN`
  when your plan includes no compute.
</Callout>

| Plan | Hosted functions |
| --- | --- |
| Free | Not included |
| Starter | 3 |
| Pro | 15 |
| Scale | Unlimited |

## The execution context [#the-execution-context]

Your function receives its validated input and a `ctx` object with two
capabilities.

**`ctx.fetch`** makes outbound HTTP requests. Every request passes an SSRF guard
that resolves the target first and refuses private, loopback and link-local
addresses — including through a redirect or a DNS record that changes between
the check and the connection. A blocked request fails with `NETWORK_DENIED`.

Each invocation may make at most 20 outbound requests, at most 5 of them
concurrently, and each is capped at 30 seconds.

**`ctx.secrets`** holds the secrets you attached to the function. They are
encrypted at rest with AES-256-GCM and only decrypted inside the isolate for the
duration of a call, so they are never part of your deployed code and never
appear in the dashboard after you save them.

## Limits [#limits]

{/* Named headers, not two blank `<th>`: empty header cells are an axe `empty-table-header` violation and leave a screen reader announcing each value with no idea what it is a value OF. Plain text rather than a visually-hidden `<span className="sr-only">` because these pages have a Markdown twin (`/docs/platform/hosted-functions.md`, lib/get-llm-text.ts) — raw JSX inside a table cell survives into it verbatim, so an agent would read the markup instead of the header. */}

| Limit             | Value                                        |
| ----------------- | -------------------------------------------- |
| Source size       | 256 KB                                       |
| Execution timeout | Your plan's maximum, or lower if you set one |
| Memory            | 64 MB, 128 MB or 256 MB, by plan             |
| Response size     | 1 MB                                         |
| Outbound requests | 20 per invocation, 5 concurrent              |

A function may declare a timeout below its plan's ceiling but never above it —
the effective limit is whichever is smaller. Exceeding memory returns
`MEMORY_EXCEEDED`: the request asked for more than the plan allows, so retrying
it unchanged cannot help.

Returning more than 1 MB fails with `INVALID_OUTPUT`. If you are producing large
results, paginate or return a reference rather than the payload.

<Callout type="warn" title="Failures here look different from the rest of the gateway">
  A hosted function's errors reach you as an edge-safe 4xx rather than the code's usual status —
  `422` for `MEMORY_EXCEEDED`, `INVALID_OUTPUT` and `OPERATION_ERROR`, `408` for
  `OPERATION_TIMEOUT` — with the original in `X-Upapi-Execution-Status`. The body is also
  `{"error": "MEMORY_EXCEEDED", …}` with `error` as a **string**, not the usual `{ code, message }`
  object. See [Errors](https://upapi.io/docs/errors#hosted-function-failures-arrive-as-a-4xx-whatever-the-code-says).
</Callout>

## Metering [#metering]

Hosted functions consume the same monthly units as any other call, plus compute
seconds against your plan's allowance. Responses carry `X-Compute-Used` and
`X-Compute-Remaining` so you can watch that budget from your client.

Exhausting the compute allowance returns `429 QUOTA_EXCEEDED` and carries no
`Retry-After`. The monthly unit quota and a trial's unit budget use that code
too, so read the message to tell which budget is gone — compute is the only one
that stops functions while marketplace operations keep working.

Hosted functions are never served from cache and never routed through the
[proxy pool](https://upapi.io/docs/platform/proxies) — `ctx.fetch` egresses directly. If you
need a specific egress IP, make the request through a marketplace operation that
supports proxying.

## Managing them [#managing-them]

Create, edit and deploy functions from
[Dashboard → Functions](https://app.upapi.io/dashboard/functions). Slugs are
lowercase and may contain letters, digits, dots and hyphens, and must start with
a letter or digit.
