upAPI Docs

Proxies

Control the egress IP an operation uses.

View as Markdown

Operations that fetch from third-party sites run through a proxy pool by default. You can override it per request, or configure a pool of your own for the whole organization.

Per request

Send the proxy URL in the x-proxy-url header:

curl -X POST https://api.upapi.io/github-trending.get \
  -H 'x-api-key: upapi_YOUR_KEY' \
  -H 'x-proxy-url: http://user:pass@proxy.example.com:8080' \
  -H 'content-type: application/json' \
  -d '{"since":"daily"}'

The URL is validated before use — it must resolve to a public address, the same guard that protects hosted functions. A URL that fails validation returns 400 INVALID_INPUT naming the reason, rather than silently falling back to the default pool.

The header works on any operation. Some operations — most of the social-platform ones — additionally declare a proxyUrl field in their input schema, which their worker honours; the operation's marketplace page shows whether it does. The header is the portable choice, since it needs nothing from the schema.

Per organization

You can also save a set of proxy URLs on your organization. When no x-proxy-url header is present, one of them is chosen at random for the call. A request header always wins over the pool.

Organization proxies are validated when you save them, so a bad entry is caught at configuration time rather than on a call at three in the morning.

Why it matters

The usual reason to bring your own proxy is IP consistency across a chain of calls. If a workflow logs in, then reads, then acts, having those steps arrive from three different addresses is what gets sessions flagged. Pinning one proxy for the chain keeps it coherent.

Proxy cost is already included in the price of a call. There is no separate proxy bill, whether you use ours or your own.

Two behaviours to design around

A custom proxy disables caching

Responses fetched through your proxy are never cached and never served from cache, so x-upapi-cache will not report a HIT. A response fetched from your egress IP is not interchangeable with one fetched from ours — geography and personalization both make it a different answer. Expect full latency and full upstream load on every proxied call.

Hosted functions are not proxied

ctx.fetch inside a hosted function egresses directly. x-proxy-url on a hosted-function call does not route its outbound requests. When you need a specific egress IP, call a marketplace operation that supports proxying instead of reimplementing the fetch in a function.