upAPI Docs

Calling operations

POST to api.upapi.io/{slug} with an x-api-key header and a JSON body: the same request shape and error envelope for every operation.

View as Markdown

Every operation is called the same way. Learn the shape once and the whole catalog is reachable — there is no per-operation base URL, no per-operation auth scheme and no per-operation response envelope to special-case.

curl -X POST https://api.upapi.io/{slug} \
  -H 'x-api-key: upapi_YOUR_KEY' \
  -H 'content-type: application/json' \
  -d '{ … }'
  • The slug is the path. It is the callable form shown in the marketplace, which is not always identical to the operation's internal id — see Quickstart.
  • The body is the input. It is validated against the operation's schema; a mismatch is 400 INVALID_INPUT and names the offending field.
  • A success returns the output object directly, with no wrapper. Failures use the standard error envelope.

That uniformity is the point of the marketplace. Swapping one operation for another means changing the slug and the body — not rewriting your client.

Try it

Pick an operation and send the request from here. The call goes straight from your browser to api.upapi.io — nothing is proxied through this site — so it runs against your own account and spends your own units, exactly as the same request would from your code.

Translate Text · costs 1 unit per call · requires text, target

Kept in this tab’s sessionStorage and sent only to api.upapi.io — it is never sent to this docs site. Create one at Dashboard → API keys.

POST https://api.upapi.io/translate-text.get

It really runs

These are live operations, not a sandbox. A call that succeeds has genuinely scraped, fetched or posted something, and it is billed at the unit weight shown next to the operation.

Idempotency

An operation is a POST that does real work upstream — a comment posted, an account created — and every accepted call spends units. So a request that times out leaves you with a genuine question: did it happen?

Send an Idempotency-Key and the answer stops mattering.

curl -X POST https://api.upapi.io/{slug} \
  -H 'x-api-key: upapi_YOUR_KEY' \
  -H 'idempotency-key: 8f14e45f-ea0f-4a2b-9d5a-5c4f2a1b7c30' \
  -H 'content-type: application/json' \
  -d '{ … }'

The first request with a given key runs normally. Any later request with the same key and the same body replays the first one's stored response, with Idempotency-Replayed: true. Nothing runs again and nothing is billed again.

  • Format — any string of 36–255 characters. A UUID is ideal. A key outside that range is 400 INVALID_INPUT.
  • Scope — your organization, for 24 hours. After that the key is forgotten and reusing it starts a new request.
  • The body is part of the identity. Reusing a key with a different slug or a different body is 409 IDEMPOTENCY_KEY_REUSED, and nothing runs — silently replaying the first answer would be answering a question you did not ask. The raw bytes are compared, so re-serializing the same JSON differently counts as a different request.
  • A key whose first request is still running answers 409 IDEMPOTENCY_IN_FLIGHT with Retry-After: 1. Retry and you will get the stored response.

A replayed response carries no quota headers — nothing was consumed, so there is no new position to report. Use GET /usage to read quota without spending.

Two outcomes are deliberately not replayable

A 429 or a 5xx releases the key instead of storing it: those say "this did not settle", and storing one would lock you out of the very retry Retry-After invited. Responses larger than ~100 KB are also not stored — a repeat of that key runs as a fresh request, and bills as one.

The TypeScript SDK sends a key on every call and reuses it across its own retries, so you get this without doing anything.

Calling from a browser

The gateway answers CORS preflights and allows any origin, so a browser can call it directly. x-api-key, content-type, idempotency-key and x-proxy-url are all accepted on a cross-origin request, and the quota headers, Retry-After, x-request-id and Idempotency-Replayed are all readable from one.

That does not make a browser a good place for your key

An API key in front-end code is a key you have published. Keep the call server-side, or proxy it through your own backend. The CORS support exists for playgrounds, internal tools and first-party dashboards where the key is already the user's own.

Where each operation is documented

This site teaches the pattern. The marketplace documents the operations:

HereOn the marketplace
The request shape, shared by everythingOne operation's input and output schema
Auth, errors, quotas, retriesField descriptions, defaults and bounds
The SDK, the MCP server, hosted functionsA worked example and a runnable playground

Each operation's page lives at upapi.io/apis/{category}/{name}, where {name} is the slug without its .get or .post suffix — so github-trending.get is at that URL, and every other slug follows the same rule. The page carries the full schema, code snippets in eight languages, and a try-it button that runs the real operation against your account.

Note the host: browsing starts on the marketplace index, which lists operations by category and by search, and each listing links across to its detail page on the app.

Machine-readable sources

If you would rather consume the catalog programmatically, these are served live from the running platform:

  • /api/catalog — every public operation with its input and output schemas, category and tags.
  • /api/openapi.json — the same catalog as an OpenAPI 3.0.3 document.
  • Each operation's marketplace page also offers its own OpenAPI slice and a Postman collection, for importing one operation rather than all of them.

That document is also enough to generate a typed client in your own language, if the TypeScript SDK is not what you want:

TypeScript
npx openapi-typescript https://app.upapi.io/api/openapi.json -o upapi.d.ts
Python
openapi-python-client generate --url https://app.upapi.io/api/openapi.json

A generated client is a snapshot: it is only as current as the document you generated it from, so re-run the command after operations are added. The TypeScript SDK is versioned and published instead, which is the difference worth knowing about.

These come from the live platform rather than from a build artifact, so they track the catalog far more closely than any page here. They are not instantaneous: the catalog is cached for 30 seconds and the OpenAPI document for an hour, and during an engine outage both fall back to a seeded mirror and say so with degraded: true. Treat that flag as "this may be stale" rather than ignoring it.

Conventions across operations

Timestamps are ISO 8601 in UTC, usually on a fetchedAt field, recording when the upstream data was read rather than when your request arrived. On a cached response (x-upapi-cache: HIT) it is the original fetch time.

Optional inputs have server-side defaults. Sending {} to an operation with no required fields is valid and uses all of them.

Proxy control is a header — x-proxy-url works on any operation. Some operations additionally declare a proxyUrl input field; the operation's marketplace page shows whether it does. See Proxies.

Cost varies per operation. Each carries a unit weight that is charged against your monthly quota — see Rate limits.