> ## Documentation Index
> Fetch the complete documentation index at: https://docs.playapi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# REST example runtime

> Configure the gateway root and shared transport for REST examples.

Use this runtime for native Gemini embeddings, tasks, Alpha Search, and Midjourney status. Run Node.js 22.18+ or 24 LTS from the documentation directory. `API_ROOT_URL` is the gateway origin plus an optional deployment prefix, **without `/v1` or `/v1beta`**. `API_KEY` is a gateway token.

```bash theme={null}
npm ci
export API_ROOT_URL="https://gateway.example.com"
export API_KEY="YOUR_API_KEY"
export API_MODEL="YOUR_MODEL_ID"
```

When copying scripts to another project, also copy `examples/http-client.mjs`. It sends Bearer credentials only to the configured gateway, rejects redirects, and rejects credentials, queries, or fragments in the base URL. Each request has a 60-second timeout including body consumption and a 1 MiB JSON limit. These are example limits; there are no automatic retries. Errors do not print raw responses or keys.

JSON requests validate HTTP, Content-Type, and error envelopes before each example validates business fields. HTTP 200 does not prove completion. The task example separately limits binary downloads to 20 MiB. After a generation transport failure, inspect any known task before submitting again.

This support module cannot call a model on its own. Existing Python / SDK examples continue to use `API_BASE_URL` including `/v1`; do not confuse the variables.

```javascript theme={null}
// Shared server-side transport for the additional REST examples.
let root;
try { root = new URL(process.env.API_ROOT_URL || 'invalid:'); }
catch { throw new Error('Set a valid API_ROOT_URL.'); }
const key = process.env.API_KEY;
if (!['http:', 'https:'].includes(root.protocol) || root.username || root.password
  || root.search || root.hash || !key?.trim()) {
  throw new Error('Set HTTP(S) API_ROOT_URL without credentials/query/fragment and API_KEY.');
}
export async function request(path, options = {}) {
  if (!path.startsWith('/') || path.startsWith('//') || /[?#\\]/.test(path)
    || path.split('/').some((part) => ['.', '..'].includes(part))) {
    throw new Error('Invalid gateway path.');
  }
  const response = await fetch(`${root.href.replace(/\/+$/, '')}${path}`, {
    ...options, redirect: 'error', signal: AbortSignal.timeout(60_000),
    headers: { ...options.headers, Authorization: `Bearer ${key}` },
  });
  if (!response.ok) {
    await response.body?.cancel();
    throw new Error(`Request failed (HTTP ${response.status}). No automatic retry.`);
  }
  return response;
}
export async function readBody(response, limit) {
  const reader = response.body?.getReader();
  if (!reader) throw new Error('Missing response body.');
  const chunks = [];
  let size = 0;
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      size += value.length;
      if (size > limit) throw new Error('Example response size limit exceeded.');
      chunks.push(value);
    }
  } finally {
    await reader.cancel();
    reader.releaseLock();
  }
  return Buffer.concat(chunks);
}
export async function requestJSON(path, body) {
  const response = await request(path, body === undefined ? {} : {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
  });
  if (!/^application\/(?:[\w.-]+\+)?json(?:\s*;|$)/i.test(response.headers.get('content-type') || '')) {
    await response.body?.cancel();
    throw new Error('Expected a JSON response.');
  }
  const result = JSON.parse((await readBody(response, 1024 * 1024)).toString('utf8'));
  if (!result || typeof result !== 'object' || Array.isArray(result) || result.error || result.success === false) {
    throw new Error('Invalid or unsuccessful JSON response.');
  }
  return result;
}
```

[阅读中文版](/zh/guides/http-runtime)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.